创建可设计的.NET组件时,需要提供默认构造函数。来自IComponent文档:
要成为一个组件,一个类必须 实现IComponent接口和 提供一个基本的构造函数 不需要参数或单个参数 IContainer类型的参数。
这使得无法通过构造函数参数进行依赖注入。 (可以提供额外的构造函数,但设计者会忽略它们。)我们正在考虑的一些替代方案:
服务定位器
不要使用依赖注入,而是使用服务定位器模式来获取依赖项。这似乎是IComponent.Site。GetService的用途。我想我们可以创建一个可重用的ISite实现(ConfigurableServiceLocator?),它可以配置必要的依赖项。但是这如何在设计师的背景下发挥作用呢?
通过属性进行依赖注入
通过属性注入依赖项。 如果是,则提供默认实例 显示组件的必要条件 设计师。记录哪些属性 需要注射。
使用Initialize方法注入依赖项
这很像注入属性,但它保留了需要在一个地方注入的依赖项列表。这样,隐式记录了所需依赖项列表,编译器将在列表更改时帮助您解决错误。
知道最佳做法是什么吗?你是怎么做到的?
<小时/> 编辑:我已删除“(例如WinForms UserControl)”,因为我打算将问题放在一般的组件上。组件都是关于控制的反转(参见UMLv2 specification的第8.3.1节),所以我认为“你不应该注入任何服务”是一个很好的答案。
<小时/> 编辑2 :花了一些玩WPF和MVVM模式来最终“得到”Mark的回答。我现在看到视觉控制确实是一个特例。至于在设计器表面上使用非可视组件,我认为.NET组件模型从根本上与依赖注入不兼容。它似乎是围绕服务定位器模式设计的。也许这将随着System.ComponentModel.Composition命名空间中.NET 4.0中添加的基础结构而改变。
答案 0 :(得分:6)
同样的问题困扰了我很长一段时间,直到我意识到我以错误的方式思考它。 AFAIR,创建IComponent实现的唯一原因是提供设计时功能 - IComponent实现没有运行时效果。
通过证据,这意味着您应该主要创建组件来实现设计时功能。特别是对于控件,这意味着您可以配置组件以某种方式运行。非常重要的是要意识到这与组件实际行为或显示的数据完全不同。它不应该在设计时具有行为,也不应该包含数据。
因此,对构造函数的约束实际上是一种祝福,因为它指示您重新考虑您的设计。控件是一种数据源不可知的软件,而不是显示器,并以某种方式与数据交互。只要该数据符合某些接口等,控制就很高兴。 如何数据到达与控制无关,也不应该如此。让Control控制数据的加载和修改方式是错误的。
在WPF中,这明显比Windows窗体更清晰:您为特定的Control提供了一个DataContext,并将Control的属性绑定到该DataContext的成员。 DataContext(可以是任何对象)源自Control外部;这是责任或你的表达层。
在Windows窗体中,您仍可以通过为Control分配数据上下文来执行相同操作。基本上,这是属性注入 - 只要知道你不应该注入服务;你应该注入数据。
总之,我不同意你的建议。相反,让Control具有一个或多个允许您将数据分配给Control的属性,并使用数据绑定来绑定此数据。在Control的实现中,准备好处理没有数据的情况:每次在设计时由VS托管Control时都会发生这种情况。 Null对象模式对于实现这种弹性非常有用。
然后,从控制器设置数据上下文。这是MVC的方式:控件是视图,但你应该有一个单独的控制器,可以实例化并将模型分配给视图。