C#中使用Activator.CreateInstance [英] C# Using Activator.CreateInstance

查看:1103
本文介绍了C#中使用Activator.CreateInstance的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

昨天我问使用任何反射或策略模式为动态调用方法有关的问题。

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;



实际上,该_class参数是在运行时传递的类的名称。
这是要做到这一点,才能有这种性能问题的常用方法?

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.

推荐答案

在使用反射,你应该问自己几个问题第一个,因为你可以在过任何─结束顶级复杂的解决方案,是很难维持:

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. 有没有使用泛型或类/接口继承来解决问题的办法

  2. 我可以解决使用动态调用(仅适用于.NET 4.0及以上)?

  3. 问题
  4. 是性能重要的是,也就是将我的表现方法或实例调用被调用一次,两次或一百万次?

  5. 我可以结合技术,以获得一个头脑聪明,但可行/理解的解决方案?

  6. 我是确定与丢失编译时类型安全?

  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?

从你的描述我假定你不知道的类型在编译的时候,你只知道它们共享接口 ICalculation 。如果这是正确的,那么数(1)和(2)中很可能无法在您的方案。

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.

这是问的一个重要问题。使用反射的开销可以阻碍超过400倍的罚款。减慢电话甚至适量

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.

分辨率是比较容易的:而不是使用 Activator.CreateInstance ,使用工厂方法(你已经有一个),查找的MethodInfo 创建一个代表,它缓存并使用该委托从那时起。这会产生只有一个点球在第一次调用,后续调用都接近本机的性能。

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.

很多有可能在这里,但我真的需要知道更多你的情况,以协助这个方向发展。通常情况下,我最终结合动态泛型,使用缓存的反思。当使用信息隐藏(如在OOP正常),你可能最终得到一个快速,稳定,仍远可扩展的解决方案。

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.

五个问题中,这也许是最重要的一种担心。这是创建自己的异常,以获得有关反射失误明确的信息很重要。这意味着:根据输入的字符串或未经检查信息的方法,构造函数或属性每次调用必须在一个try / catch包裹。只捕获特定的异常(一如既往,我的意思是:永远也赶不上例外本身)。

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).

聚焦 TargetException (方法不存在), TargetInvocationException (方法存在,但上涨的EXC。调用时), TargetParameterCountException MethodAccessException (不正确的权限,发生在ASP.NET很多),出现InvalidOperationException (与泛型类型发生)。你并不总是需要努力赶上所有的人,这取决于预期投入和预期目标对象。

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.

摆脱你的 Activator.CreateInstance 和使用的MethodInfo找到工厂创建方法,并使用委派。 createDelegate方法以创建并缓存委托。只需将其存储在一个静态词典,其中最关键的是等于在您的示例代码的类字符串。以下是安全的这样做,并没有失去太多类型安全快速但不那么肮脏的方式。

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.

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

这篇关于C#中使用Activator.CreateInstance的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆