C# 使用 Activator.CreateInstance

发布于 2024-10-21 17:44:41 字数 1148 浏览 2 评论 0原文

我昨天问了一个关于使用反射或策略模式动态调用方法的问题。

然而,从那时起,我决定将这些方法更改为实现公共接口的单独类。原因是,每个类虽然有一些相似之处,但也执行该类特有的某些方法。

我一直在使用这样的策略:

switch (method)
{
    case "Pivot":
        return new Pivot(originalData);
    case "GroupBy":
        return new GroupBy(originalData);
    case "Standard deviation":
        return new StandardDeviation(originalData);
    case "% phospho PRAS Protein":
        return new PhosphoPRASPercentage(originalData);
    case "AveragePPPperTreatment":
        return new AveragePPPperTreatment(originalData);
    case "AvgPPPNControl":
        return new AvgPPPNControl(originalData);
    case "PercentageInhibition":
        return new PercentageInhibition(originalData);
    default:
        throw new Exception("ERROR: Method " + method + " does not exist.");
}

但是,随着潜在类数量的增长,我将需要不断添加新类,从而打破了修改关闭规则。

相反,我使用了这样的解决方案:

var test = Activator.CreateInstance(null, "MBDDXDataViews."+ _class);
       ICalculation instance = (ICalculation)test.Unwrap();
       return instance;

实际上,_class 参数是运行时传入的类的名称。 这是执行此操作的常见方法吗?这会产生性能问题吗?

我对反思还很陌生,所以欢迎您的建议。

I asked a question yesterday regarding using either reflection or Strategy Pattern for dynamically calling methods.

However, since then I have decided to change the methods into individual classes that implement a common interface. The reason being, each class, whilst bearing some similarities also perform certain methods unique to that class.

I had been using a strategy as such:

switch (method)
{
    case "Pivot":
        return new Pivot(originalData);
    case "GroupBy":
        return new GroupBy(originalData);
    case "Standard deviation":
        return new StandardDeviation(originalData);
    case "% phospho PRAS Protein":
        return new PhosphoPRASPercentage(originalData);
    case "AveragePPPperTreatment":
        return new AveragePPPperTreatment(originalData);
    case "AvgPPPNControl":
        return new AvgPPPNControl(originalData);
    case "PercentageInhibition":
        return new PercentageInhibition(originalData);
    default:
        throw new Exception("ERROR: Method " + method + " does not exist.");
}

However, as the number of potential classes grow, I will need to keep adding new ones, thus breaking the closed for modification rule.

Instead, I have used a solution as such:

var test = Activator.CreateInstance(null, "MBDDXDataViews."+ _class);
       ICalculation instance = (ICalculation)test.Unwrap();
       return instance;

Effectively, the _class parameter is the name of the class passed in at runtime.
Is this a common way to do this, will there be any performance issues with this?

I am fairly new to reflection, so your advice would be welcome.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

云柯 2024-10-28 17:44:41

使用反射时,您应该首先问自己几个问题,因为您可能最终会得到一个难以维护的过于复杂的解决方案:

  1. 是否有一种方法可以使用通用性或类/接口继承来解决问题?
  2. 我可以使用动态调用解决问题(仅限.NET 4.0及更高版本)吗?
  3. 性能重要吗?即我的反射方法或实例化调用会被调用一次、两次还是一百万次?
  4. 我可以结合技术来获得智能但可行/易于理解的解决方案吗?
  5. 我可以接受失去编译时类型安全性吗?

通用性/动态

根据您的描述,我假设您在编译时不知道类型,您只知道它们共享接口ICalculation。如果这是正确的,那么上面的数字 (1) 和 (2) 在您的场景中可能是不可能的。

性能

这是一个重要的问题。使用反射的开销可能会带来超过 400 倍的损失:即使是中等数量的调用也会减慢速度。

解决方案相对简单:不使用 Activator.CreateInstance,而是使用工厂方法(您已经拥有该方法),查找 MethodInfo 创建委托,将其缓存并使用从那时起,代表。这只会对第一次调用产生惩罚,后续调用具有接近本机的性能。

结合技术

这里有很多可能,但我真的需要更多地了解您的情况才能在这个方向上提供帮助。通常,我最终会将动态与泛型和缓存反射结合起来。当使用信息隐藏时(这在 OOP 中很常见),您最终可能会得到一个快速、稳定且可扩展性良好的解决方案。

失去编译时类型安全

在五个问题中,这可能是最需要担心的一个。创建自己的异常以提供有关反射错误的清晰信息非常重要。这意味着:基于输入字符串或其他未经检查的信息对方法、构造函数或属性的每次调用都必须包装在 try/catch 中。仅捕获特定异常(一如既往,我的意思是:永远不要捕获 Exception 本身)。

重点关注 TargetException(方法不存在)、TargetInitationException(方法存在,但在调用时出现异常)、TargetParameterCountExceptionMethodAccessException(不是正确的权限,在 ASP.NET 中经常发生)、InvalidOperationException(发生于泛型类型)。您并不总是需要尝试捕获所有它们,这取决于预期的输入和预期的目标对象。

总结一下,

摆脱 Activator.CreateInstance 并使用 MethodInfo 查找工厂创建方法,然后使用 Delegate.CreateDelegate 创建并缓存委托。只需将其存储在静态字典中,其中键等于示例代码中的类字符串。下面是一种快速但不那么肮脏的方法,可以安全地完成此操作,并且不会失去太多类型安全性。

示例代码

public class TestDynamicFactory
{
    // static storage
    private static Dictionary<string, Func<ICalculate>> InstanceCreateCache = new Dictionary<string, Func<ICalculate>>();

    // how to invoke it
    static int Main()
    {
        // invoke it, this is lightning fast and the first-time cache will be arranged
        // also, no need to give the full method anymore, just the classname, as we
        // use an interface for the rest. Almost full type safety!
        ICalculate instanceOfCalculator = this.CreateCachableICalculate("RandomNumber");
        int result = instanceOfCalculator.ExecuteCalculation();
    }

    // searches for the class, initiates it (calls factory method) and returns the instance
    // TODO: add a lot of error handling!
    ICalculate CreateCachableICalculate(string className)
    {
        if(!InstanceCreateCache.ContainsKey(className))
        {
            // get the type (several ways exist, this is an eays one)
            Type type = TypeDelegator.GetType("TestDynamicFactory." + className);

            // NOTE: this can be tempting, but do NOT use the following, because you cannot 
            // create a delegate from a ctor and will loose many performance benefits
            //ConstructorInfo constructorInfo = type.GetConstructor(Type.EmptyTypes);

            // works with public instance/static methods
            MethodInfo mi = type.GetMethod("Create");

            // the "magic", turn it into a delegate
            var createInstanceDelegate = (Func<ICalculate>) Delegate.CreateDelegate(typeof (Func<ICalculate>), mi);

            // store for future reference
            InstanceCreateCache.Add(className, createInstanceDelegate);
        }

        return InstanceCreateCache[className].Invoke();

    }
}

// example of your ICalculate interface
public interface ICalculate
{
    void Initialize();
    int ExecuteCalculation();
}

// example of an ICalculate class
public class RandomNumber : ICalculate
{
    private static Random  _random;

    public static RandomNumber Create()
    {
        var random = new RandomNumber();
        random.Initialize();
        return random;
    }

    public void Initialize()
    {
        _random = new Random(DateTime.Now.Millisecond);
    }

    public int ExecuteCalculation()
    {
        return _random.Next();
    }
}

When using reflection you should ask yourself a couple of questions first, because you may end up in an over-the-top complex solution that's hard to maintain:

  1. Is there a way to solve the problem using genericity or class/interface inheritance?
  2. Can I solve the problem using dynamic invocations (only .NET 4.0 and above)?
  3. Is performance important, i.e. will my reflected method or instantiation call be called once, twice or a million times?
  4. Can I combine technologies to get to a smart but workable/understandable solution?
  5. Am I ok with losing compile time type safety?

Genericity / dynamic

From your description I assume you do not know the types at compile time, you only know they share the interface ICalculation. If this is correct, then number (1) and (2) above are likely not possible in your scenario.

Performance

This is an important question to ask. The overhead of using reflection can impede a more than 400-fold penalty: that slows down even a moderate amount of calls.

The resolution is relatively easy: instead of using Activator.CreateInstance, use a factory method (you already have that), look up the MethodInfo create a delegate, cache it and use the delegate from then on. This yields only a penalty on the first invocation, subsequent invocations have near-native performance.

Combine technologies

A lot is possible here, but I'd really need to know more of your situation to assist in this direction. Often, I end up combining dynamic with generics, with cached reflection. When using information hiding (as is normal in OOP), you may end up with a fast, stable and still well-extensible solution.

Losing compile time type safety

Of the five questions, this is perhaps the most important one to worry about. It is very important to create your own exceptions that give clear information about reflection mistakes. That means: every call to a method, constructor or property based on an input string or otherwise unchecked information must be wrapped in a try/catch. Catch only specific exceptions (as always, I mean: never catch Exception itself).

Focus on TargetException (method does not exist), TargetInvocationException (method exists, but rose an exc. when invoked), TargetParameterCountException, MethodAccessException (not the right privileges, happens a lot in ASP.NET), InvalidOperationException (happens with generic types). You don't always need to try to catch all of them, it depends on the expected input and expected target objects.

To sum it up

Get rid of your Activator.CreateInstance and use MethodInfo to find the factory-create method, and use Delegate.CreateDelegate to create and cache the delegate. Simply store it in a static Dictionary where the key is equal to the class-string in your example code. Below is a quick but not-so-dirty way of doing this safely and without losing too much type safety.

Sample code

public class TestDynamicFactory
{
    // static storage
    private static Dictionary<string, Func<ICalculate>> InstanceCreateCache = new Dictionary<string, Func<ICalculate>>();

    // how to invoke it
    static int Main()
    {
        // invoke it, this is lightning fast and the first-time cache will be arranged
        // also, no need to give the full method anymore, just the classname, as we
        // use an interface for the rest. Almost full type safety!
        ICalculate instanceOfCalculator = this.CreateCachableICalculate("RandomNumber");
        int result = instanceOfCalculator.ExecuteCalculation();
    }

    // searches for the class, initiates it (calls factory method) and returns the instance
    // TODO: add a lot of error handling!
    ICalculate CreateCachableICalculate(string className)
    {
        if(!InstanceCreateCache.ContainsKey(className))
        {
            // get the type (several ways exist, this is an eays one)
            Type type = TypeDelegator.GetType("TestDynamicFactory." + className);

            // NOTE: this can be tempting, but do NOT use the following, because you cannot 
            // create a delegate from a ctor and will loose many performance benefits
            //ConstructorInfo constructorInfo = type.GetConstructor(Type.EmptyTypes);

            // works with public instance/static methods
            MethodInfo mi = type.GetMethod("Create");

            // the "magic", turn it into a delegate
            var createInstanceDelegate = (Func<ICalculate>) Delegate.CreateDelegate(typeof (Func<ICalculate>), mi);

            // store for future reference
            InstanceCreateCache.Add(className, createInstanceDelegate);
        }

        return InstanceCreateCache[className].Invoke();

    }
}

// example of your ICalculate interface
public interface ICalculate
{
    void Initialize();
    int ExecuteCalculation();
}

// example of an ICalculate class
public class RandomNumber : ICalculate
{
    private static Random  _random;

    public static RandomNumber Create()
    {
        var random = new RandomNumber();
        random.Initialize();
        return random;
    }

    public void Initialize()
    {
        _random = new Random(DateTime.Now.Millisecond);
    }

    public int ExecuteCalculation()
    {
        return _random.Next();
    }
}
凉城 2024-10-28 17:44:41

我建议您为工厂实现提供一个方法RegisterImplementation。因此,每个新类只是对该方法的调用,并且您不会更改工厂代码。

更新:
我的意思是这样的:

创建一个定义计算的接口。根据您的代码,您已经这样做了。为了完整起见,我将在答案的其余部分中使用以下接口:

public interface ICalculation
{
    void Initialize(string originalData);
    void DoWork();
}

您的工厂将如下所示:

public class CalculationFactory
{
    private readonly Dictionary<string, Func<string, ICalculation>> _calculations = 
                        new Dictionary<string, Func<string, ICalculation>>();

    public void RegisterCalculation<T>(string method)
        where T : ICalculation, new()
    {
        _calculations.Add(method, originalData =>
                                  {
                                      var calculation = new T();
                                      calculation.Initialize(originalData);
                                      return calculation;
                                  });
    }

    public ICalculation CreateInstance(string method, string originalData)
    {
        return _calculations[method](originalData);
    }
}

出于简单性的原因,这个简单的工厂类缺乏错误检查。

更新2:
您可以在应用程序初始化例程中的某个地方像这样初始化它:

CalculationFactory _factory = new CalculationFactory();

public void RegisterCalculations()
{
    _factory.RegisterCalculation<Pivot>("Pivot");
    _factory.RegisterCalculation<GroupBy>("GroupBy");
    _factory.RegisterCalculation<StandardDeviation>("Standard deviation");
    _factory.RegisterCalculation<PhosphoPRASPercentage>("% phospho PRAS Protein");
    _factory.RegisterCalculation<AveragePPPperTreatment>("AveragePPPperTreatment");
    _factory.RegisterCalculation<AvgPPPNControl>("AvgPPPNControl");
    _factory.RegisterCalculation<PercentageInhibition>("PercentageInhibition");
}

I suggest you give your factory implementation a method RegisterImplementation. So every new class is just a call to that method and you are not changing your factories code.

UPDATE:
What I mean is something like this:

Create an interface that defines a calculation. According to your code, you already did this. For the sake of being complete, I am going to use the following interface in the rest of my answer:

public interface ICalculation
{
    void Initialize(string originalData);
    void DoWork();
}

Your factory will look something like this:

public class CalculationFactory
{
    private readonly Dictionary<string, Func<string, ICalculation>> _calculations = 
                        new Dictionary<string, Func<string, ICalculation>>();

    public void RegisterCalculation<T>(string method)
        where T : ICalculation, new()
    {
        _calculations.Add(method, originalData =>
                                  {
                                      var calculation = new T();
                                      calculation.Initialize(originalData);
                                      return calculation;
                                  });
    }

    public ICalculation CreateInstance(string method, string originalData)
    {
        return _calculations[method](originalData);
    }
}

This simple factory class is lacking error checking for the reason of simplicity.

UPDATE 2:
You would initialize it like this somewhere in your applications initialization routine:

CalculationFactory _factory = new CalculationFactory();

public void RegisterCalculations()
{
    _factory.RegisterCalculation<Pivot>("Pivot");
    _factory.RegisterCalculation<GroupBy>("GroupBy");
    _factory.RegisterCalculation<StandardDeviation>("Standard deviation");
    _factory.RegisterCalculation<PhosphoPRASPercentage>("% phospho PRAS Protein");
    _factory.RegisterCalculation<AveragePPPperTreatment>("AveragePPPperTreatment");
    _factory.RegisterCalculation<AvgPPPNControl>("AvgPPPNControl");
    _factory.RegisterCalculation<PercentageInhibition>("PercentageInhibition");
}
蓝颜夕 2024-10-28 17:44:41

作为一个例子,如何在构造函数中添加初始化:

类似于:

Activator.CreateInstance(Type.GetType("ConsoleApplication1.Operation1"), initializationData);

but written with Linq Expression, part of code is taken here:

public class Operation1 
{
    public Operation1(object data)
    { 
    }
}

public class Operation2 
{
    public Operation2(object data)
    {
    }
}

public class ActivatorsStorage
{
    public delegate object ObjectActivator(params object[] args);

    private readonly Dictionary<string, ObjectActivator> activators = new Dictionary<string,ObjectActivator>();

    private ObjectActivator CreateActivator(ConstructorInfo ctor)
    {
        Type type = ctor.DeclaringType;

        ParameterInfo[] paramsInfo = ctor.GetParameters();
        ParameterExpression param = Expression.Parameter(typeof(object[]), "args");

        Expression[] argsExp = new Expression[paramsInfo.Length];

        for (int i = 0; i < paramsInfo.Length; i++)
        {
            Expression index = Expression.Constant(i);
            Type paramType = paramsInfo[i].ParameterType;

            Expression paramAccessorExp = Expression.ArrayIndex(param, index);

            Expression paramCastExp = Expression.Convert(paramAccessorExp, paramType);

            argsExp[i] = paramCastExp;
        }

        NewExpression newExp = Expression.New(ctor, argsExp);

        LambdaExpression lambda = Expression.Lambda(typeof(ObjectActivator), newExp, param);

        return (ObjectActivator)lambda.Compile();
    }

    private ObjectActivator CreateActivator(string className)
    {
        Type type = Type.GetType(className);
        if (type == null)
            throw new ArgumentException("Incorrect class name", "className");

        // Get contructor with one parameter
        ConstructorInfo ctor = type.GetConstructors()
            .SingleOrDefault(w => w.GetParameters().Length == 1 
                && w.GetParameters()[0].ParameterType == typeof(object));

        if (ctor == null)
                throw new Exception("There is no any constructor with 1 object parameter.");

        return CreateActivator(ctor);
    }

    public ObjectActivator GetActivator(string className)
    {
        ObjectActivator activator;

        if (activators.TryGetValue(className, out activator))
        {
            return activator;
        }
        activator = CreateActivator(className);
        activators[className] = activator;
        return activator;
    }
}

用法如下:

ActivatorsStorage ast = new ActivatorsStorage();
var a = ast.GetActivator("ConsoleApplication1.Operation1")(initializationData);
var b = ast.GetActivator("ConsoleApplication1.Operation2")(initializationData);

同样可以用DynamicMethods来实现。

此外,这些类不需要从同一接口或基类继承。

谢谢,维塔利

Just as an example how to add initialization in the constructor:

Something similar to:

Activator.CreateInstance(Type.GetType("ConsoleApplication1.Operation1"), initializationData);

but written with Linq Expression, part of code is taken here:

public class Operation1 
{
    public Operation1(object data)
    { 
    }
}

public class Operation2 
{
    public Operation2(object data)
    {
    }
}

public class ActivatorsStorage
{
    public delegate object ObjectActivator(params object[] args);

    private readonly Dictionary<string, ObjectActivator> activators = new Dictionary<string,ObjectActivator>();

    private ObjectActivator CreateActivator(ConstructorInfo ctor)
    {
        Type type = ctor.DeclaringType;

        ParameterInfo[] paramsInfo = ctor.GetParameters();
        ParameterExpression param = Expression.Parameter(typeof(object[]), "args");

        Expression[] argsExp = new Expression[paramsInfo.Length];

        for (int i = 0; i < paramsInfo.Length; i++)
        {
            Expression index = Expression.Constant(i);
            Type paramType = paramsInfo[i].ParameterType;

            Expression paramAccessorExp = Expression.ArrayIndex(param, index);

            Expression paramCastExp = Expression.Convert(paramAccessorExp, paramType);

            argsExp[i] = paramCastExp;
        }

        NewExpression newExp = Expression.New(ctor, argsExp);

        LambdaExpression lambda = Expression.Lambda(typeof(ObjectActivator), newExp, param);

        return (ObjectActivator)lambda.Compile();
    }

    private ObjectActivator CreateActivator(string className)
    {
        Type type = Type.GetType(className);
        if (type == null)
            throw new ArgumentException("Incorrect class name", "className");

        // Get contructor with one parameter
        ConstructorInfo ctor = type.GetConstructors()
            .SingleOrDefault(w => w.GetParameters().Length == 1 
                && w.GetParameters()[0].ParameterType == typeof(object));

        if (ctor == null)
                throw new Exception("There is no any constructor with 1 object parameter.");

        return CreateActivator(ctor);
    }

    public ObjectActivator GetActivator(string className)
    {
        ObjectActivator activator;

        if (activators.TryGetValue(className, out activator))
        {
            return activator;
        }
        activator = CreateActivator(className);
        activators[className] = activator;
        return activator;
    }
}

The usage is following:

ActivatorsStorage ast = new ActivatorsStorage();
var a = ast.GetActivator("ConsoleApplication1.Operation1")(initializationData);
var b = ast.GetActivator("ConsoleApplication1.Operation2")(initializationData);

The same can be implemented with DynamicMethods.

Also, the classes are not required to be inherited from the same interface or base class.

Thanks, Vitaliy

九局 2024-10-28 17:44:41

在这种情况下,我使用的一种策略是使用特殊属性来标记我的各种实现以指示其键,并扫描活动程序集以查找具有该键的类型:

[AttributeUsage(AttributeTargets.Class)]
public class OperationAttribute : System.Attribute
{ 
    public OperationAttribute(string opKey)
    {
        _opKey = opKey;
    }

    private string _opKey;
    public string OpKey {get {return _opKey;}}
}

[Operation("Standard deviation")]
public class StandardDeviation : IOperation
{
    public void Initialize(object originalData)
    {
        //...
    }
}

public interface IOperation
{
    void Initialize(object originalData);
}

public class OperationFactory
{
    static OperationFactory()
    {
        _opTypesByKey = 
            (from a in AppDomain.CurrentDomain.GetAssemblies()
             from t in a.GetTypes()
             let att = t.GetCustomAttributes(typeof(OperationAttribute), false).FirstOrDefault()
             where att != null
             select new { ((OperationAttribute)att).OpKey, t})
             .ToDictionary(e => e.OpKey, e => e.t);
    }
    private static IDictionary<string, Type> _opTypesByKey;
    public IOperation GetOperation(string opKey, object originalData)
    {
        var op = (IOperation)Activator.CreateInstance(_opTypesByKey[opKey]);
        op.Initialize(originalData);
        return op;
    }
}

这样,只需使用新的键字符串创建一个新类,您可以自动“插入”工厂,而无需修改工厂代码。

您还会注意到,我没有依赖每个实现来提供特定的构造函数,而是在我希望类实现的接口上创建了一个 Initialize 方法。只要他们实现了该接口,我就能够将“originalData”发送给他们,而不会出现任何反射怪异。

我还建议使用像 Ninject 这样的依赖注入框架,而不是使用 Activator.CreateInstance。这样,您的操作实现就可以使用构造函数注入来实现其各种依赖项。

One strategy that I use in cases like this is to flag my various implementations with a special attribute to indicate its key, and scan the active assemblies for types with that key:

[AttributeUsage(AttributeTargets.Class)]
public class OperationAttribute : System.Attribute
{ 
    public OperationAttribute(string opKey)
    {
        _opKey = opKey;
    }

    private string _opKey;
    public string OpKey {get {return _opKey;}}
}

[Operation("Standard deviation")]
public class StandardDeviation : IOperation
{
    public void Initialize(object originalData)
    {
        //...
    }
}

public interface IOperation
{
    void Initialize(object originalData);
}

public class OperationFactory
{
    static OperationFactory()
    {
        _opTypesByKey = 
            (from a in AppDomain.CurrentDomain.GetAssemblies()
             from t in a.GetTypes()
             let att = t.GetCustomAttributes(typeof(OperationAttribute), false).FirstOrDefault()
             where att != null
             select new { ((OperationAttribute)att).OpKey, t})
             .ToDictionary(e => e.OpKey, e => e.t);
    }
    private static IDictionary<string, Type> _opTypesByKey;
    public IOperation GetOperation(string opKey, object originalData)
    {
        var op = (IOperation)Activator.CreateInstance(_opTypesByKey[opKey]);
        op.Initialize(originalData);
        return op;
    }
}

That way, just by creating a new class with a new key string, you can automatically "plug in" to the factory, without having to modify the factory code at all.

You'll also notice that rather than depending on each implementation to provide a specific constructor, I've created an Initialize method on the interface I expect the classes to implement. As long as they implement the interface, I'll be able to send the "originalData" to them without any reflection weirdness.

I'd also suggest using a dependency injection framework like Ninject instead of using Activator.CreateInstance. That way, your operation implementations can use constructor injection for their various dependencies.

吖咩 2024-10-28 17:44:41

本质上,听起来您想要工厂模式。在这种情况下,您定义输入到输出类型的映射,然后像您正在做的那样在运行时实例化该类型。

示例:

您有 X 个类,它们都共享 IDoSomething 的公共接口。

public interface IDoSomething
{
     void DoSomething();
}

public class Foo : IDoSomething
{
    public void DoSomething()
    {
         // Does Something specific to Foo
    }
}

public class Bar : IDoSomething
{
    public void DoSomething()
    {
        // Does something specific to Bar
    }
}

public class MyClassFactory
{
     private static Dictionary<string, Type> _mapping = new Dictionary<string, Type>();

     static MyClassFactory()
     {
          _mapping.Add("Foo", typeof(Foo));
          _mapping.Add("Bar", typeof(Bar));
     }

     public static void AddMapping(string query, Type concreteType)
     {
          // Omitting key checking code, etc. Basically, you can register new types at runtime as well.
          _mapping.Add(query, concreteType);
     }

     public IDoSomething GetMySomething(string desiredThing)
     {
          if(!_mapping.ContainsKey(desiredThing))
              throw new ApplicationException("No mapping is defined for: " + desiredThing);

          return Activator.CreateInstance(_mapping[desiredThing]) as IDoSomething;
     }
}

Essentially, it sounds like you want the factory pattern. In this situation, you define a mapping of input to output types and then instantiate the type at runtime like you are doing.

Example:

You have X number of classes, and they all share a common interface of IDoSomething.

public interface IDoSomething
{
     void DoSomething();
}

public class Foo : IDoSomething
{
    public void DoSomething()
    {
         // Does Something specific to Foo
    }
}

public class Bar : IDoSomething
{
    public void DoSomething()
    {
        // Does something specific to Bar
    }
}

public class MyClassFactory
{
     private static Dictionary<string, Type> _mapping = new Dictionary<string, Type>();

     static MyClassFactory()
     {
          _mapping.Add("Foo", typeof(Foo));
          _mapping.Add("Bar", typeof(Bar));
     }

     public static void AddMapping(string query, Type concreteType)
     {
          // Omitting key checking code, etc. Basically, you can register new types at runtime as well.
          _mapping.Add(query, concreteType);
     }

     public IDoSomething GetMySomething(string desiredThing)
     {
          if(!_mapping.ContainsKey(desiredThing))
              throw new ApplicationException("No mapping is defined for: " + desiredThing);

          return Activator.CreateInstance(_mapping[desiredThing]) as IDoSomething;
     }
}
南巷近海 2024-10-28 17:44:41
  1. 这里没有错误检查。您绝对确定 _class 将解析为有效的类吗?您是否控制所有可能的值,或者该字符串是否以某种方式由最终用户填充?
  2. 反思通常比避免反思成本最高。性能问题与您计划以这种方式实例化的对象数量成正比。
  3. 在您运行并使用依赖注入框架之前,请阅读对其的批评。 =)
  1. There's no error checking here. Are you absolutely sure that _class will resolve to a valid class? Are you controlling all the possible values or does this string somehow get populated by an end-user?
  2. Reflection is generally most costly than avoiding it. Performance issues are proportionate to the number of objects you plan to instantiate this way.
  3. Before you run off and use a dependency injection framework read the criticisms of it. =)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文