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

C# 通过反射解决的构造函数注入循环依赖?,c#,reflection,dependency-injection,C#,Reflection,Dependency Injection,在回答我的问题时,这是关于循环依赖的一半: 演示者依赖于IView。页面(实现IView)取决于演示者。其他人已经解决了从构造函数注入到属性注入的转换问题 我不喜欢这一点,因为我觉得你现在需要公开可以在外部修改的属性,这也成为开发者初始化的责任 但我似乎已经能够用另一种方式解决这个问题 通过使用默认构造函数和重载参数化构造函数,可以通过反射调用无参数构造函数来创建对象 然后通过注入依赖项调用重载的一个 我将举例说明: class TestObject { public string N

在回答我的问题时,这是关于循环依赖的一半:

演示者依赖于IView。页面(实现IView)取决于演示者。其他人已经解决了从构造函数注入到属性注入的转换问题

我不喜欢这一点,因为我觉得你现在需要公开可以在外部修改的属性,这也成为开发者初始化的责任

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

通过使用默认构造函数和重载参数化构造函数,可以通过反射调用无参数构造函数来创建对象 然后通过注入依赖项调用重载的一个

我将举例说明:

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>();
但是我可以通过反射使用重载构造函数来注入依赖项:

ConstructorInfo ctor = obj.GetType().GetConstructors()[1];
ctor.Invoke(obj, new[] { "injected" });
我能够使用这种方法连接structuremap,在创建后注册实例,然后注入依赖项

当然,我们可以使用常规方法来注入依赖项,但这再次打破了封装,因此您可以调用它 再次尝试覆盖依赖项

同样,作为构造函数,静态代码不能简单地实现这一点

但我不知道我对此有何感想,这感觉有点像黑客或者我可以在C#中偶然做的事情

我想听听你的想法


谢谢。

我不喜欢使用反射,因为它意味着如果没有IoC容器,就无法轻松创建视图/演示者

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

但是,此时可以使用一个简单的属性或
初始化(视图)
/
初始化(演示者)
方法。调用这些方法的责任被工厂或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;
    }
}

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

我不喜欢使用反射,因为它意味着如果没有IoC容器,就无法轻松创建视图/演示者

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

但是,此时可以使用一个简单的属性或
初始化(视图)
/
初始化(演示者)
方法。调用这些方法的责任被工厂或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;
    }
}

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

有趣的是,这是可能的,但我认为这需要长期维护。想象一下,一个可怜的维护工程师正在调试你的代码,他看到构造函数被调用了两次,并假设两个实例一定是在某处以某种方式创建的。相关:这是可能的,这很有趣,但我认为这需要长期的维护。想象一下,一个可怜的维护工程师正在调试你的代码,他看到构造函数被调用了两次,并假设两个实例一定是在某个地方以某种方式创建的。首先,这里的目标是DI ASP.NET。您不能使无参数构造函数受到保护,页面是由ASP创建的,对此您没有任何控制权,您所能做的就是在处理程序工厂中将依赖项注入到已创建的对象中。工厂是一个很好的解决方案,事实上它是解决方案之一,但我在我的帖子中指出,这需要开发人员为他的每个演示者创建一个工厂,也许你可以完全通过泛型来实现,但我仍在努力解决这个问题Chris Chilvers的想法很有用,尤其是当
Initialize
方法公开时。当您的
System.Web.Page
类可以按约定使用公共
Initialize
方法时,您可以使用反射查找该方法,并将所需的依赖项注入其中。尽管这仍然需要反思,但这将在部分信任的情况下起作用。在已初始化的对象上调用重载构造函数将无法在部分信任中工作。尽管如此,输入错误
initialize
,没有注入任何内容。克里斯,如果我怀疑你的答案,很抱歉,史蒂文,感谢你让我意识到这一点,是的,这确实有效,让我意识到,当你将初始化视为惯例,从而使工厂知道调用什么时,这已经接近了,因为它似乎不是建设性的,我发现你的回答很有用,我将采用这个解决方案,尽管我同意你说的有一个或两个问题。首先,这里的目标是DI ASP.NET。您不能使无参数构造函数受到保护,页面是由ASP创建的,对此您没有任何控制权,您所能做的就是在处理程序工厂中将依赖项注入到已创建的对象中。这家工厂是一个很好的解决方案,事实上它
public class TestObject
{
    protected TestObject () {
    }

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

    protected Initialize (string name) {}
}