我有两个组件; AssemblyWithInterface
和AssemblyWithClass
。在AssemblyWithInterface
中,我有一个名为IDoSomething
的界面,该界面由TheClass
中的AssemblyWithClass
实施。 (AssemblyWithClass
引用AssemblyWithInterface
。)
在AssemblyWithInterface
中我想使用反射从AssemblyWithClass
创建类的实例:
var theAssembly = Assembly.Load("Company.AssemblyWithClass, { FQN... }");
var theConcreteClass = theAssembly.CreateInstance("Company.AssemblyWithClass.TheClass");
程序集加载正常,实例创建为TheConcreteClass
。
但是,我无法将theConcreteClass
强制转换为其实现界面。我在这里得到InvalidCastException
:
var theConcreteClassInterfaced = (IDoSomething)theConcreteClass;
和
var isAssignable = typeof(IDoSomething).IsAssignableFrom(theConcreteClass.GetType();
是假的。
我错过了什么? (目标是使用命令模式样式的功能将实现IDoSomething
的命令添加到AssemblyWithClass
,并且能够在AssemblyWithInterface
中执行它们,而无需更改AssemblyWithInterface
中的代码}。)
平台是.NET 3.5(不能使用动态)。
更新 这个问题的背景(解释为什么我不遵守DIP)是我有一个遗留的ASP.NET应用程序包含在一个大型程序集中。我想创建一个插件程序集,它可以调用遗留程序集的各个部分来执行监视和一些自动化任务。我不希望任何其他依赖项(对其他程序集的引用)添加到旧程序集中。我们的想法是在遗留程序集中实现一个钩子(一个新的特殊页面和一个IPlugInOperation),添加一个带有相应代码的监视页面。让代码执行各种IPlugInOperations(绘制一个接口以允许管理员指定用于在旧程序集中执行代码的参数)。 PlugIn程序集必须引用旧程序集,旧程序集使用反射列表,并允许管理员执行PlugIn程序集中包含的IPlugInOperation的各种实现。
答案 0 :(得分:3)
接口不应该关心实现。
重构并将所有逻辑移动到第三个程序集。
<强>更新强>
规范汇编
类汇编(参考规范)
应用程序集(引用两者)
public class Program
{
private ICommandFactory _commandFactory;
public static void Main(string[] argv)
{
// this is the only line that is really dependent of a specific
// implementation.
_commandFactory = new TheSpecificImplementationAssembly.CommandFactory();
ICommand command = _commandFactory.Create("CreateUser");
command.Execute();
}
}
<强> UPDATE2 强>
大多数现代解决方案使用控制容器的反转来处理接口和实现之间的映射。
另一种解决方案是拥有一小组工厂,用于为特定接口创建实现。在这种情况下,我还将使用工厂方法模式让聚合根能够创建子聚合。例如,类Order
将有一个名为CreateOrderLine
的方法,该方法将返回IOrderLine
个对象。
答案 1 :(得分:1)
这里有一种循环引用,这绝对不是理想的,但应该可以做你想要的事情。看看AppDomain.AssemblyResolve。我怀疑正在发生的事情是反映实例化类导致系统加载原始接口程序集的额外副本,因此它实现的接口类型不再是静态引用的类型。通过实现AssemblyResolve,您可以使用AppDomain.GetAssemblies搜索已加载的程序集列表,并返回具有匹配名称的程序集。
以下是一种可能的实施方式:
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
return AppDomain.CurrentDomain.GetAssemblies().
FirstOrDefault(assembly => assembly.FullName == args.Name);
}