假设我有几个接口实现:
public interface IDemoInterface
{
void DemoMethod();
}
public class DemoClass1 : IDemoInterface
{
public void DemoMethod()
{
Console.WriteLine("DemoClass1");
}
}
public class DemoClass2 : IDemoInterface
{
public void DemoMethod()
{
Console.WriteLine("DemoClass2");
}
}
现在,我正在尝试定义此类实现的数组,并为每个实现调用接口方法。基本上,做这样的事情(以下是一个非工作代码 - 只是我想要做的事情的例证):
public static void Run()
{
var demoClasses = { DemoClass1, DemoClass2};
foreach (var demoClass in demoClasses)
{
var implementation = demoClass as IDemoInterface;
implementation.DemoMethod();
}
}
实现它的最佳方法是什么?是否只使用Reflection?
答案 0 :(得分:5)
从您的示例中,您似乎并未尝试遍历实现接口的类的实例。你试图循环遍历类型本身。我是基于这个......
var demoClasses = { DemoClass1, DemoClass2};
...在您的问题中DemoClass
和DemoClass2
是类类型,而不是类型的实例。
您可以从一组类型开始,然后创建每种类型的实例,如下所示。 (您也可以使用反射来查找实现您的界面的类类型。)
var types = new Type[] {typeof(DemoClass1), typeof(DemoClass1)};
foreach (var type in types)
{
if (typeof(IDemoInterface).IsAssignableFrom(type))
{
var instance = Activator.CreateInstance(type) as IDemoInterface;
instance.DemoMethod();
}
}
这会有效,但是有一个很大的限制:每个类都必须有一个默认的构造函数。或者如果你要打电话给构造函数,他们都需要有相同的构造函数。
换句话说,如果其中一个类有这样的构造函数:
public DemoClass1(string connectionString){
然后Activator.CreateInstance
会爆炸,除非你给它传递给构造函数的值。但是如果你有一个类型的数组,并且它们有不同的构造函数,那么这几乎是不可能的。而且你不想把自己画成一个角落,你必须编写只能有空构造函数的类。
当你有一个实现这些接口的接口和类,并且你想获得这些类的实例时,依赖注入容器(Ioc容器)可能会有所帮助。如果您还没有使用它,那么学习曲线就会有一点点,但它是一个非常有用的工具。以下是将my blog容器配置为“register”的示例(这是Windsor),或者声明接口的多个实现。
(如果你不熟悉它,它似乎完全不合适,这就是为什么我也链接到Windsor的文档。)
public class DependencyRegistration : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel));
container.Register(Component.For<OrderValidator>());
container.Register(
Component.For<IOrderValidator,AddressValidator>()
.Named("AddressValidator"),
Component.For<IOrderValidator,OrderLinesValidator>()
.Named("OrderLinesValidator"),
Component.For<IOrderValidator,ProductStateRestrictionsValidator>()
.Named("ProductStateRestrictionsValidator")
);
}
}
在这个例子中,我告诉这个“容器”,有三个类实现IOrderValidator
。
现在,这个容器可以“解析”或返回IOrderValidator
的一系列实现。请注意顶部附近的这条线:
container.Register(Component.For<OrderValidator>());
这是班级:
public class OrderValidator : IOrderValidator
{
private readonly IOrderValidator[] _subValidators;
public OrderValidator(IOrderValidator[] subValidators)
{
_subValidators = subValidators;
}
public IEnumerable<ValidationMessage> GetValidationMessages(Order order)
{
var validationMessages = new List<ValidationMessage>();
foreach(var validator in _subValidators)
{
validationMessages.AddRange(validator.GetValidationMessages(order));
}
return validationMessages;
}
}
请注意,在构造函数中有一个IOrderValidator
数组。如果我打电话
var validator = container.Resolve<OrderValidator>();
容器会创建OrderValidator
的实例。它会检测到构造函数需要一个IOrderValidator
数组,因此它会创建所有其他类的实例并将它们放在数组中。
如果这些类中的任何一个也有需要其他值或类的构造函数,并且我告诉容器如何创建它们,那么它将根据需要创建它们以便能够创建IOrderValidator
的实现。
结果是我可以有很多类的实现,每个类都有不同的构造函数依赖项,我可以创建这些实现的集合。
这个答案并没有真正告诉你如何做到这一点,但希望它能说明在哪里可以找到完成这类工作的工具。正确使用DI容器是非常有用的工具。这在大型应用程序甚至是小型应用程序中都很常见,因为它可以更轻松地管理许多类型,这些类型可以互相嵌套,同时保持理智和可理解。它还有助于我们编写更小,更容易进行单元测试的类。
除了Windsor之外,其他一些容器还包括Autofac,Unity,SimpleInjector以及ASP.NET核心应用程序中包含的DI容器。
答案 1 :(得分:3)
您的代码大部分都是正确的:
private static void Main()
{
var demoClasses = new List<IDemoInterface> {new DemoClass1(), new DemoClass2()};
foreach (var demoClass in demoClasses)
{
demoClass.DemoMethod();
}
Console.Write("\nDone!\nPress any key to exit...");
Console.ReadKey();
}
<强>输出强>
答案 2 :(得分:2)
从问题来看,我不清楚您是希望自己初始化类型,还是想要像Rufus's回答那样做。
但是,如果您事先不知道实现,理论上可以扫描它们,并检索所有类型
// can throw a ReflectionTypeLoadException in case not all dlls are known
private static IEnumerable<Type> GetImplementationsOf<TInterface>() {
var interfaceType = typeof( TInterface );
return AppDomain.CurrentDomain.GetAssemblies()
.Select( assembly => assembly.GetTypes().Where( type => !type.IsInterface && interfaceType.IsAssignableFrom( type ) ) )
.SelectMany( implementation => implementation );
}
此代码将返回与通用类型参数匹配的IEnumerable<Type>
,并且它不是一个接口(虽然理论上,这部分可以让您的消费者处理)
之后,您可以浏览类型列表并创建它们的实例并运行您的代码,如下所示:
var types = GetImplementationsOf<IDemoInterface>();
foreach (var type in types) {
// will throw an exception in case there is no parameterless constructor
var impl = (IDemoInterface) Activator.CreateInstance( type );
impl.DemoMethod();
}