构造函数注入循环依赖通过反射解决?

发布于 2024-12-19 14:04:19 字数 1361 浏览 1 评论 0原文

为了回答我在这里的问题,这是循环依赖的一半:

C# ASP.NET 依赖注入与 IoC 容器并发症

演示器依赖于 IView。并且Page(实现IView)依赖于presenter。其他人已经解决了从构造函数注入到属性注入的转换,

我不喜欢这个,因为我觉得你现在需要创建可以在外部修改的公共属性,并且也成为开发人员初始化的责任。

但似乎我已经能够通过另一种方式解决这个问题。

通过页面具有默认构造函数以及重载的有参构造函数,您可以通过反射调用无参构造函数来创建对象 然后通过注入依赖项来调用重载的。

我将通过示例说明:

class TestObject
{
    public string Name { get; set; }

    public TestObject()
    {
       Name = "Constructed with no args";
       Console.WriteLine("constructor hash code: " + GetHashCode());
    }

    public TestObject(string name)
    {
        this.Name = name;
        Console.WriteLine("constructor hash code: " + GetHashCode());
    }
}

这个对象可以简单地构造:

var obj = Activator.CreateInstance<TestObject>();

或者

var obj = new TestObject();

但是然后我可以通过反射使用重载的构造函数来注入依赖项:

ConstructorInfo ctor = obj.GetType().GetConstructors()[1];
ctor.Invoke(obj, new[] { "injected" });

我能够使用这种方法来连接结构图以在创建后注册实例,然后注入的依赖关系。

当然我们可以使用常规方法来注入依赖项,但这又有点破坏了封装,所以你可以这样调用 再次覆盖依赖项。

另外,作为构造函数,这不能简单地通过静态代码来实现。

但我不知道我对此有何感受,感觉有点像黑客或我可以在 C# 中意外完成的事情。

我想听听你的想法

谢谢。

In response to my question here that was half way about having a circular dependency:

C# ASP.NET Dependency Injection with IoC Container Complications

The presenter depends on an IView. And the Page (implementing the IView) depends on a presenter. Other have solved this converting from constructor injection to property injection

I didnt like this because I felt that you now need to make public properties that can be modified externally, and also becomes the developers responsibilityies to initialise.

But it seems that I have been able to solve this another way.

By the page having a default constructor as well as an overloaded parameterised constructor, you can via reflection call the parameterless constructor to create the object
then call the overloaded one by injecting the dependencies.

I'ill illustrate by example:

class TestObject
{
    public string Name { get; set; }

    public TestObject()
    {
       Name = "Constructed with no args";
       Console.WriteLine("constructor hash code: " + GetHashCode());
    }

    public TestObject(string name)
    {
        this.Name = name;
        Console.WriteLine("constructor hash code: " + GetHashCode());
    }
}

This object can be constructed simply:

var obj = Activator.CreateInstance<TestObject>();

or

var obj = new TestObject();

But then I can via reflection use the overloaded constructor to inject the dependencies:

ConstructorInfo ctor = obj.GetType().GetConstructors()[1];
ctor.Invoke(obj, new[] { "injected" });

I was able to use this approach to wire up structuremap to register the instance after creation and then inject the dependencies.

Of course we can use a regular method to inject the dependencies, but this again breaks the encapsulation a bit, so you could call this
again to override dependencies.

Also as constructor this cant simply be reached by static code.

But I have no idea how I feel about this, it feels a little bit like a hack or something that i can simply do accidentally in C#.

I would like to hear your thoughts

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

花伊自在美 2024-12-26 14:04:19

我不喜欢这种反射的使用,因为这意味着您无法在没有 IoC 容器的情况下轻松创建视图/演示者。

解决 IoC 容器需求的一种方法是使用工厂轻松创建视图/呈现器并将反射逻辑放置在工厂中。

但是,此时您可以有一个简单的属性或 Initialize(view)/Initialize(presenter) 方法。工厂或 IoC 容器(充当工厂)剥夺了开发人员调用这些方法的责任。

在构造时将依赖关系传递给对象的另一种方法是传递一个可以构建对象依赖关系的工厂。工厂最简单的形式是一个简单的Func

void Main()
{
    var controller = Controller.Create (c => new View (c));  
}

class Controller {
    private View view;

    // This could also be a constructor, but I prefer to think of this
    // as a factory method.
    public static Controller Create (Func<Controller, View> viewBuilder) {
        var controller = new Controller ();
        var view = viewBuilder (controller);
        controller.Initialize (view);
        return controller;
    }

    protected Controller() {
    }

    protected void Initialize (View view) {
        this.view = view;
    }
}

class View {
    private Controller controller;

    public View (Controller controller) {
        this.controller = controller;
    }
}

如果您确实坚持拥有一对构造函数,我会将无参数构造函数保护起来,除非在没有演示器/视图的情况下创建视图/演示器确实有效。这可以防止有人错误地使用该构造函数,因为在构造之后没有反射就无法初始化视图/演示者。

尽管我认为这在反射思想上稍微好一点,那就是创建一个受保护的无参数构造函数和一个受保护的初始化方法。

public class TestObject
{
    protected TestObject () {
    }

    public TestObject (string name) {
        Initialize (name)
    }

    protected Initialize (string name) {}
}

这强制你不能在没有名称的情况下构造它,除非你使用反射。反射将被封装在一个工厂中,这将确保 Initialize 也被调用。

I don't like this use of reflection as it means you can't easily create a view/presenter with out a an IoC container.

One way of solving the need for an IoC container is to use a factory to easily create a view/presenter and placing the reflection logic in the factory.

However, at this point you could have a simple property or Initialize(view)/Initialize(presenter) method. The responsibility for calling these methods is taken away from the developers by the factory or the IoC container (which is acting as a factory).

An alternative to passing the dependency to the object when constructing is to pass an factory that can build the dependency to the object. The simplest form of factory is a simple Func.

void Main()
{
    var controller = Controller.Create (c => new View (c));  
}

class Controller {
    private View view;

    // This could also be a constructor, but I prefer to think of this
    // as a factory method.
    public static Controller Create (Func<Controller, View> viewBuilder) {
        var controller = new Controller ();
        var view = viewBuilder (controller);
        controller.Initialize (view);
        return controller;
    }

    protected Controller() {
    }

    protected void Initialize (View view) {
        this.view = view;
    }
}

class View {
    private Controller controller;

    public View (Controller controller) {
        this.controller = controller;
    }
}

If you do insist upon having a pair of constructors I would make the no argument constructor protected unless it really is valid to create the view/presenter without a presenter/view. This would prevent someone using that constructor by mistake, since there's no way without reflection to initialize the view/presenter after construction.

Though I then think this is a slightly better on the reflection idea would be to make a parameterless constructor that is protected, and a protected initialize method.

public class TestObject
{
    protected TestObject () {
    }

    public TestObject (string name) {
        Initialize (name)
    }

    protected Initialize (string name) {}
}

This enforces that you can't construct it without a name, unless you use reflection. The reflection would be encapsulated in a factory which would ensure Initialize is also called.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文