C# 动态加载来自不同程序集的类(具有自定义行为)?

C# 动态加载来自不同程序集的类(具有自定义行为)?,c#,.net,dynamic-loading,C#,.net,Dynamic Loading,我们正在为少数客户构建一个应用程序,每个客户都有自己的需求,还有相似的需求。我们还希望将所有代码保留在同一个应用程序中,而不是分支,而IFs不是一个好的选择,因为它将无处不在 我计划为所有人提供基类。然后,每个客户都有自己的类,其中重写方法将执行特殊的逻辑 我们如何在编译时加载程序集而不是这样做 public class BaseClass { public string getEventId() } public class ClassForJohn:BaseClass {

我们正在为少数客户构建一个应用程序,每个客户都有自己的需求,还有相似的需求。我们还希望将所有代码保留在同一个应用程序中,而不是分支,而IFs不是一个好的选择,因为它将无处不在

我计划为所有人提供基类。然后,每个客户都有自己的类,其中重写方法将执行特殊的逻辑

我们如何在编译时加载程序集而不是这样做

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();
}

这里有一种使用DI处理它的方法

IUnityContainer container=newunitycontainer();
字符串customerNamespace=ConfigurationManager.AppSettings[“customerNamespace”];
container.RegisterType(typeof(接口),
Type.GetType(customerNamespace+“.SomeImplementation”);
// ...
ISomeInterface实例=conainer.Resolve();

每个客户在特定于客户的命名空间中都有自己的
接口实现。

您可以通过以下方式从程序集创建外部类型的实例:

object obj = Activator.CreateInstance( 
    "External.Assembly.Name", "External.Assembly.Name.TypeName");
BaseClass b = (BaseClass) obj;
b.getEventId();

您可以将程序集的名称和类型存储在配置文件或其他适当的位置。

每个客户是否都有自己的exe和config文件,并且有一个共享的dll?或者有一个共享的exe,每个客户都有自己的dll

您可以在配置中输入完整的类型名称,如下所示:

Shared.exe.config:


以下是我如何将插件(外接程序)加载到我的一个项目中:

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;
}
conststringplugintypename=“MyCompany.MyProject.Contracts.IMyPlugin”;
///从DLL文件加载所有插件。
///DLL的文件名,例如“C:\Prog\MyApp\MyPlugIn.DLL”
///插件对象的列表。
///一个DLL可以包含多个实现“IMyPlugin”的类型。
公共列表LoadPluginsFromFile(字符串文件名)
{
装配asm;
IMyPlugin插件;
列出插件;
型着色面;
插件=新列表();
asm=Assembly.LoadFrom(文件名);
foreach(在asm.GetExportedTypes()中键入t){
tInterface=t.GetInterface(PluginTypeName);
if(tInterface!=null&&(t.Attributes&TypeAttributes.Abstract)=
类型属性(摘要){
plugin=(IMyPlugin)Activator.CreateInstance(t);
plugins.Add(plugin);
}
}
返回插件;
}
我假设每个插件实现
IMyPlugin
。您可以按任何方式定义此接口。如果循环浏览插件文件夹中包含的所有DLL并调用此方法,则可以自动加载所有可用插件


通常至少有三个程序集:一个包含接口定义,一个引用此接口程序集的主程序集,以及至少一个实现(当然也引用)此接口的程序集。

我将使用Unity,但作为一个简单的工厂

你可以把你的钱存起来

我使用的是Unity.2.1.505.2(以防万一,这会有所不同)


这是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); 
UnityContainer container=newunitycontainer();
UnityConfiguration节=(UnityConfiguration节)ConfigurationManager.GetSection(“unity”);
配置(容器);
字符串myKey=“John”/*从配置文件*/*读取此示例中,值应为“myCarKey”或“myTruckKey”*/
IVehicle v1=container.Resolve(myKey);
见:


也许这个例子会有帮助

public MyInterface GetNewType() { 
       Type type = Type.GetType( "MyClass", true ); 
       object newInstance = Activator.CreateInstance( type ); 
       return newInstance as MyInterface; 
    } 

MEF在这里似乎是个不错的选择@在.NET4之前,虽然花钱不多,但不可用,而且我确信有“不那么进取”或“不那么这样”的替代方案。您考虑过使用依赖注入吗?您可以在应用程序启动时基于给定的客户配置依赖项。@jrummell,但要配置哪个DI提供程序;和/或,举个例子?DI和动态加载是两件不同的事情——在其核心,这个问题可以用工厂+动态加载来回答。那么,回到起点:如何从附属程序集动态加载类型?如何加载程序集/类型和创建实例:以及-我已经暗示这是“最低要求”,尽管DI和组合框架可以帮助总体设计。至少,我肯定会推荐DI,但请看这些问题的直接答案。使用了MEF和StructureMap后,我可以通过自己的思考来加载程序集和扩展。这太简单了。我不会向任何人推荐MEF,除非这对RYO程序集管理框架来说是个大问题?一个.dll文件?文件夹名称?DLL的完整路径,例如
“C:\Program Files\MyApp\MyPlugIn.DLL”
。我们希望实现相同类型的情况,即一个程序集中包含“标准”代码,但通过创建包含子类的客户特定程序集,为某些客户自定义该代码。子类将实现来自标准程序集的接口。我们计划将程序集和类型信息存储在数据库中,以便支持人员可以为每个客户进行配置。然后在运行时,我们可以加载正确的程序集(如果在数据库中指定)并实例化正确的子类型?
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;
}
  <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>
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); 
public MyInterface GetNewType() { 
       Type type = Type.GetType( "MyClass", true ); 
       object newInstance = Activator.CreateInstance( type ); 
       return newInstance as MyInterface; 
    }