从不同的程序集动态加载类(具有自定义行为)
本文关键字:自定义 程序集 加载 动态 | 更新日期: 2023-09-27 18:01:32
我们正在为少数客户构建一个应用程序,每个客户都有自己的要求以及类似的要求。我们还希望将所有代码保留在同一个应用程序中,而不是分支它,并且if不是一个好的选择,因为它将在所有地方。
我计划拥有所有的基类。然后,每个客户将拥有自己的类,其中重写方法将执行特殊逻辑。
如何在编译时加载程序集而不是这样做
public class BaseClass {
public string getEventId()
}
public class ClassForJohn:BaseClass {
[override]
public string getEventId()
}
public class ClassForAdam:BaseClass {
[override]
public string getEventId()
}
void UglyBranchingLogicSomewhere() {
BaseClass eventOject;
if("John"==ConfigurationManager.AppSettings["CustomerName"]){
eventOject = new ClassForJohn();
}else if("Adam"==ConfigurationManager.AppSettings["CustomerName"]){
eventOject = new ClassForAdam();
}else{
eventOject = new BaseClass ();
}
eventId = eventOject.getEventId();
}
我是这样将插件(add-ins)加载到我的一个项目中的:
const string PluginTypeName = "MyCompany.MyProject.Contracts.IMyPlugin";
/// <summary>Loads all plugins from a DLL file.</summary>
/// <param name="fileName">The filename of a DLL, e.g. "C:'Prog'MyApp'MyPlugIn.dll"</param>
/// <returns>A list of plugin objects.</returns>
/// <remarks>One DLL can contain several types which implement `IMyPlugin`.</remarks>
public List<IMyPlugin> LoadPluginsFromFile(string fileName)
{
Assembly asm;
IMyPlugin plugin;
List<IMyPlugin> plugins;
Type tInterface;
plugins = new List<IMyPlugin>();
asm = Assembly.LoadFrom(fileName);
foreach (Type t in asm.GetExportedTypes()) {
tInterface = t.GetInterface(PluginTypeName);
if (tInterface != null && (t.Attributes & TypeAttributes.Abstract) !=
TypeAttributes.Abstract) {
plugin = (IMyPlugin)Activator.CreateInstance(t);
plugins.Add(plugin);
}
}
return plugins;
}
我假设每个插件都实现IMyPlugin
。您可以以任何您想要的方式定义这个接口。如果循环遍历插件文件夹中包含的所有dll并调用此方法,则可以自动加载所有可用的插件。
通常你会有至少三个程序集:一个包含接口定义,主程序集引用这个接口程序集,至少一个程序集实现(当然也引用)这个接口。
是否每个客户都有自己的exe和配置文件,并且有一个共享的dll?还是有一个共享的exe,每个客户都有自己的dll?
您可以在配置中像这样放置完整的类型名称:
Shared.exe.config:
<appSettings>
<add key="CustomerType" value="NamespaceForJohn.ClassForJohn, AssemblyForJohn"/>
</appSettings>
并将AssemblyForJohn.dll与Shared.exe放在同一文件夹中。
然后你可以像这样在代码中动态加载它:
Shared.exe:
var typeString = ConfigurationManager.AppSettings["CustomerType"];
var parts = typeString.Split(',');
var typeName = parts[0];
var assemblyName = parts[1];
var instance = (BaseClass)Activator.CreateInstance(assemblyName, typeName).Unwrap();
也许这个例子会有帮助
public MyInterface GetNewType() {
Type type = Type.GetType( "MyClass", true );
object newInstance = Activator.CreateInstance( type );
return newInstance as MyInterface;
}
这是使用Unity使用DI处理它的一种方法。
IUnityContainer container = new UnityContainer();
string customerNamespace = ConfigurationManager.AppSettings["CustomerNamespace"];
container.RegisterType(typeof(ISomeInterface),
Type.GetType(customerNamespace+".SomeImplementation"));
// ...
ISomeInterface instance = conainer.Resolve<ISomeInterface>();
每个客户在客户特定的名称空间中都有自己的ISomeInterface
实现。
您可以通过以下方式从程序集创建外部类型的实例:
object obj = Activator.CreateInstance(
"External.Assembly.Name", "External.Assembly.Name.TypeName");
BaseClass b = (BaseClass) obj;
b.getEventId();
您可以将程序集的名称和类型存储在配置文件或其他适当的位置。
我会使用Unity,但作为一个简单的工厂。
Unity框架:如何从同一个接口实例化两个类?
你可以存储
我使用的是Unity.2.1.505.2(只是为了防止产生差异)。
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>
</configSections>
<unity>
<container>
<register type="IVehicle" mapTo="Car" name="myCarKey" />
<register type="IVehicle" mapTo="Truck" name="myTruckKey" />
</container>
</unity>
这是DotNet代码。
UnityContainer container = new UnityContainer();
UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
section.Configure(container);
string myKey = "John"; /* read from config file */ /* with this example, the value should be "myCarKey" or "myTruckKey" */
IVehicle v1 = container.Resolve<IVehicle>(myKey);
:
http://msdn.microsoft.com/en-us/library/ff664762 (v = pandp.50) . aspx
和
http://www.sharpfellows.com/post/Unity-IoC-Container-.aspx