简体   繁体   English

使用反射的工厂模式-使用与每个类关联的静态键进行类注册

[英]Factory pattern using reflection - class registration using static key associated with each class

While studying some patterns I came across the factory pattern to create instances of (unknown) classes. 在研究一些模式时,我遇到了工厂模式以创建(未知)类的实例。 That made me interested hence I want to build a program that can be dynamically enhanced. 这使我产生了兴趣,因此我想构建一个可以动态增强的程序。 So I have some basic functionality within one assembly and the actual workers within different assemblies. 因此,我在一个程序集中具有一些基本功能,而在不同程序集中具有实际工人。 The only hint that I get from my application is an operation-name. 我从应用程序获得的唯一提示是操作名称。 Now I want to create a new worker that relies to the operation. 现在,我想创建一个依赖于该操作的新工作程序。 Because I do not know the actual implementations of the workers I've chosen this kind of pattern. 因为我不知道这些工人的实际实现方式,所以选择了这种模式。

OK, some more details: OK,更多细节:

I have an IWorker -interface that all my processes implement. 我的所有流程都实现了一个IWorker A Singleton WorkerFactory lets me create new instances of any worker implemented in any assembly within the same path as the current one. Singleton WorkerFactory使我可以在与当前组件相同的路径中的任何程序WorkerFactory创建实现的任何worker的新实例。 To do so I added a CreateWorker -method to my interface that accept an array of strings (which have been passed as varargs to the console-application). 为此,我在接口中添加了一个CreateWorker方法,该方法接受一个字符串数组(这些字符串已作为varargs传递到控制台应用程序)。

Before being able to create any instance of any worker I have to register any class to the factory. 在能够创建任何工人的任何实例之前,我必须向工厂注册任何类。 Unfortunately on C# it is not possible to insert such a behaviour statically into any static context of the worker-classes because the code within this context is only executed when the class is accessed in any way (be it by instantiating it or by accessing any of its static members, see static constructors ). 不幸的是,在C#上,无法将这种行为静态地插入到工作者类的任何静态上下文中,因为仅当以任何方式访问该类时(无论是通过实例化它还是通过访问任何一个此类),都将执行此上下文中的代码其静态成员,请参见静态构造函数 )。 So I achieve this by reflecting all the types found within all assemblies with the path of the currently executing assembly. 因此,我通过用当前正在执行的程序集的路径反映所有程序集内找到的所有类型来实现这一点。 Now I may check if the current type is implementing the interface and if so the type can be added to the registration-map of the factory containing the operations name (key) and the worker (value) as System.Type (see Factory Pattern using reflection ). 现在,我可以检查当前类型是否正在实现该接口,如果可以,则可以将该类型作为System.Type添加到包含操作名称(键)和worker(值)的工厂的注册映射中(请参阅使用反射 )。 The problem is: how do I get the actual name of the operation this type refers to. 问题是:如何获取此类型所指操作的实际名称。

I think there are two options for this: 我认为有两种选择:

  1. I add a static property to every worker-class that stores the operations name. 我向存储操作名称的每个工人类添加一个静态属性。 Thus I can refer the operation by reflecting this property on the obtained type. 因此,我可以通过在获得的类型上反映此属性来引用该操作。
  2. Adding a non-static property for the name and create a dummy-instance of the actual worker-class while registering. 为该名称添加一个非静态属性,并在注册时创建实际工作人员类的虚拟实例。

While the former option has the disadvantage that I cannot ensure that all workers actually have such a static property implemented (hence it is not possible to add a static member to the IWorker-interface) the latter has the disadvantage that every class must have an empty constructor in order to build the dummy-instance from which to get the operations name. 尽管前者的缺点是我无法确保所有工作程序实际上都实现了这样的静态属性(因此无法向IWorker接口添加静态成员),但后者的缺点是每个类都必须有一个空构造函数,以构建从中获取操作名称的虚拟实例。 Nevertheless this constructor would be publicly accessable whilst not containing any initialization-code. 但是,此构造函数将可以公开访问,同时不包含任何初始化代码。

Having said that all this question is about best or at least better practices on factory pattern using reflection. 话虽如此,所有这些问题都是关于使用反射的工厂模式的最佳实践,或者至少是最佳实践。 I think I´d prefer the first option, but maybe I also missed any better solution for this approach. 我认为我更喜欢第一种选择,但是也许我也错过了这种方法的任何更好的解决方案。

Hence this thread is already quite long I´d like not to post any code here, but let me know if you need anyway. 因此,该线程已经很长了,我不想在这里发布任何代码,但是无论如何请告诉我。

You can use custom attributes on your worker classes: 您可以在工作程序类上使用自定义属性

[AttributeUsage(System.AttributeTargets.Class)]
public class WorkerAttribute : Attribute
{
    public WorkerAttribute (String operationType)
    {
       this.OperationType = operationType; 
    }

    public String OperationType {get; private set;}
}

public interface IWorker { }

[WorkerAttribute("Experienced")]
public class ExperiencedWorker: IWorker
{
}

They are accessed like it is described here . 可以按此处所述访问它们。 It will be something like: 它将类似于:

       Type attributeType = typeof(WorkerAttribute);

        var myWorkerClassesPlusAttributes = from assembly in AppDomain.CurrentDomain.GetAssemblies()
                                            from type in assembly.GetTypes()
                                            let attributes = type.GetCustomAttributes(attributeType, true)
                                            where attributes.Any()
                                            select 
                                                new KeyValuePair<String, Type>(((WorkerAttribute)attributes.First()).OperationType, 
                                                                               type);

        Dictionary<String, Type> workers = new Dictionary<string, Type>();
        foreach (var item in myWorkerClassesPlusAttributes)
            workers.Add(item.Key, item.Value);

        IWorker worker = (IWorker)Activator.CreateInstance(workers["Experienced"]);

It is not the best and does not cover multiple WorkerAttributes on the class, but can be used as the basis for your solution. 它不是最好的,并且不包含该类上的多个WorkerAttributes,但可以用作解决方案的基础。

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

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