简体   繁体   English

Activator.CreateInstance:类的动态实例化

[英]Activator.CreateInstance: Dynamic Instantiation of Classes

I am designing a loosely-coupled structure. 我正在设计一个松耦合的结构。 I want to call classes from different assemblies/namespaces via a code which is represented by a String . 我想通过String表示的代码从不同的程序集/命名空间中调用类 My design is, each of client's business rules is on different assemblies and not dependent on each other (ONE client is to ONE DLL ratio) so that when I made an update on business rules of 1 client, it would not affect the others. 我的设计是,每个客户的业务规则都在不同的程序集上并且彼此不依赖(一个客户与一个DLL的比率),因此当我对1个客户的业务规则进行更新时,不会影响其他客户。 My attention now is on using Factory Design and using Activator.CreateInstance() Method. 我现在的注意力是在使用Factory Design和Activator.CreateInstance()方法。

This is the project setup (2+n DLL's) 这是项目设置(2 + n个DLL)

 namespace Foundation; // where the interfaces/abstract resides
 namespace Factory; // has dependency on Foundation assembly
 namespace Client1; // client1's DLL, no dependency
 namespace Client2; // client2's DLL, no dependency
 The UI // only referenced to the Foundation and Factory not the Clients

The actual code 实际代码

 namespace Foundation
 {
   public interface IBusinessRules
   {
     string GetBusinessRule();
  }
 }

 namespace Client1 //DLL for client 1
 {
   public class BusinessRules : Foundation.IBusinessRules
   {
    public string GetBusinessRule()
    {
        return "Client1 Business Rule";
    }
   }
}

namespace Client2 //DLL for client 2
{
   public class BusinessRules : Foundation.IBusinessRules
   {
     public string GetBusinessRule()
     {
        return "Client2 Business Rule";
     }
   }
}


namespace Factory
{
  public static class Invoker<T> where T: Foundation.IBusinessRules
  {
    public static T FetchInstance(string clientCode) 
    {
        return (T)Activator.CreateInstance(Type.GetType(clientCode));   
    }
  }
}


 //sample implementation that generates unhandled Exception
 using Factory;
 using Foundation;
 static void Main(string[] args)
 {
      //the parameter is maintained in the database
       IBusinessRules objClient1 = Invoker<IBusinessRules>.FetchInstance("Client1"); 

       //should call Client1.BusinessRules method
        Console.WriteLine(objClient.GetBusinessRule());  
        Console.Read();

        objClient = Invoker<IBusinessRules>.FetchInstance("Client2");

        //should call Client2.BusinessRules method
        Console.WriteLine(objClient.GetBusinessRule()); 
        Console.Read();      
  }

Any idea why my sample doesn't work? 知道为什么我的样本不起作用吗? And any suggestion to improve the design? 还有改进设计的建议吗? Thanks in advance. 提前致谢。

How about using 怎么样使用

Expression.Lambda Expression.Lambda

anyone? 任何人?

If you use FetchInstance("Client.BusinessRules") your code works, IF everything is in the same assembly. 如果您使用FetchInstance(“ Client.BusinessRules”),则如果所有内容都在同一程序集中,则代码可以正常工作。 If it's not (as per your design) you need to give an AssemblyQualifiedName . 如果不是(按照您的设计),则需要提供AssemblyQualifiedName

I would do the design differently though. 但是我会做不同的设计。 Keep your call with just "Client1" as Parameter but change the implementation of the Factory. 仅使用“ Client1”作为参数保持通话,但更改Factory的实现。 Dynamically load the assembly for the given client (with Assembly.Load() or Assembly.LoadFrom()), then use clientAssembly.CreateInstance() to istantiate your type. 动态加载给定客户端的程序集(使用Assembly.Load()或Assembly.LoadFrom()),然后使用clientAssembly.CreateInstance()来确定您的类型。

Edit: Crude code sample: 编辑:原始代码示例:

namespace Factory
{
  public static class Invoker<T> where T: IBusinessRules
  {
    public static T FetchInstance(string clientCode)
    {
        var clientAssembly = Assembly.LoadFrom(clientCode + ".dll");

        return (T)clientAssembly.CreateInstance(clientCode+".BusinessRules");
    }
  }
}

If you dont't know the class name in the client-dll, you have to search for an applicable Type, for example with clientAssembly.GetTypes(). 如果您不知道client-dll中的类名称,则必须搜索适用的Type,例如,使用clientAssembly.GetTypes()。

Thanks to your help guys i finally Got it! 多亏了您的帮助,我终于明白了! I just modify the Factory 我只是修改工厂

 namespace Factory
 {
    public static class Invoker<T> where T : Foundation.IBusinessRules
    {
        public static T FetchInstance(string clientCode)
        {
           Type objType = Type.GetType(clientCode + ".BusinessRules," + clientCode);
           return (T)Activator.CreateInstance(objType);

    }
}

But I wonder about its effeciency (performance hit) because it uses Reflection.. 但是我不知道它的效率(性能下降),因为它使用了反射。

You need to use the full name of the class. 您需要使用该类的全名。

for example: 例如:

Type.GetType("System.Collections.Generic.Dictionary`2[System.String,[MyType,MyAssembly]]")

If you are loading the type from an external assembly, I would recommend using Activator.CreateInstanceFrom . 如果要从外部程序Activator.CreateInstanceFrom加载类型,则建议使用Activator.CreateInstanceFrom

var typeReference = Activator.CreateInstanceFrom(assemblyPath, fullyQualifiedClassName);
return typeReference.Unwrap() as T;

If you want to be able to add business rules as dlls after deployment and create them at runtime, I suggest you have a business rules folder under you app, load all dlls in that app, search for all types that implement of IBusinessRules in each dll using reflection. 如果您希望能够在部署后将业务规则添加为dll并在运行时创建它们,则建议您在应用程序下面有一个业务规则文件夹,在该应用程序中加载所有dll,在每个dll中搜索实现IBusinessRules的所有类型使用反射。 Given that you now have handles on the types, creating one based on name would be easy and your project would scale out. 既然您现在可以处理类型了,那么基于名称创建一个就很容易了,您的项目也可以扩展。

Either that, or pass the assembly qualified name of the classes to your methods. 或者,或者将类的程序集限定名称传递给您的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM