如何在C#中使用自定义属性来替换交换机中的交换机?

use*_*239 5 c# custom-attributes switch-statement

我有一个工厂方法,根据三个枚举值返回正确的子类.一种方法是使用开关中的开关.显然,我不太喜欢这个选项.

我认为另一种选择是在C#中使用属性.每个子类都有一个具有3个枚举值的属性,并且在工厂中我只需要获得具有与工厂中的枚举值相对应的相同枚举值的类.

但是,我对属性很新,我在网上找不到任何合适的解决方案.如果有人,可以给我一些提示或一些代码行,我真的很感激!

Mir*_*Mir 4

首先,声明您的属性并将其添加到您的类中。

enum MyEnum
{
    Undefined,
    Set,
    Reset
}

class MyEnumAttribute : Attribute
{
    public MyEnumAttribute(MyEnum value)
    {
        Value = value;
    }

    public MyEnum Value { get; private set; }
}

[MyEnum(MyEnum.Reset)]
class ResetClass
{
}

[MyEnum(MyEnum.Set)]
class SetClass
{
}

[MyEnum(MyEnum.Undefined)]
class UndefinedClass
{
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用此代码使用枚举和类型创建字典,并动态创建类型。

//Populate a dictionary with Reflection
var dictionary = Assembly.GetExecutingAssembly().GetTypes().
    Select(t => new {t, Attribute = t.GetCustomAttribute(typeof (MyEnumAttribute))}).
    Where(e => e.Attribute != null).
    ToDictionary(e => (e.Attribute as MyEnumAttribute).Value, e => e.t);
//Assume that you dynamically want an instance of ResetClass
var wanted = MyEnum.Reset;
var instance = Activator.CreateInstance(dictionary[wanted]);
//The biggest downside is that instance will be of type object.
//My solution in this case was making each of those classes implement
//an interface or derive from a base class, so that their signatures
//would remain the same, but their behaviors would differ.
Run Code Online (Sandbox Code Playgroud)

您可能会注意到,调用的Activator.CreateInstance性能并不好。因此,如果您想稍微提高性能,您可以将字典更改为,Dictionary<MyEnum,Func<object>>而不是添加类型作为值,而是添加包装每个类的构造函数并将它们作为对象返回的函数。

编辑:我正在添加一个ConstructorFactory改编自此页面的课程。

static class ConstructorFactory
{
    static ObjectActivator<T> GetActivator<T>(ConstructorInfo ctor)
    {
        var paramsInfo = ctor.GetParameters();
        var param = Expression.Parameter(typeof(object[]), "args");
        var argsExp = new Expression[paramsInfo.Length];
        for (var i = 0; i < paramsInfo.Length; i++)
        {
            Expression index = Expression.Constant(i);
            var paramType = paramsInfo[i].ParameterType;
            Expression paramAccessorExp = Expression.ArrayIndex(param, index);
            Expression paramCastExp = Expression.Convert(paramAccessorExp, paramType);
            argsExp[i] = paramCastExp;
        }
        var newExp = Expression.New(ctor, argsExp);
        var lambda = Expression.Lambda(typeof(ObjectActivator<T>), newExp, param);
        var compiled = (ObjectActivator<T>)lambda.Compile();
        return compiled;
    }

    public static Func<T> Create<T>(Type destType)
    {
        var ctor = destType.GetConstructors(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).First();
        Func<ConstructorInfo, object> activatorMethod = GetActivator<Type>;
        var method = typeof(ConstructorFactory).GetMethod(activatorMethod.Method.Name, BindingFlags.Static | BindingFlags.NonPublic);
        var generic = method.MakeGenericMethod(destType);
        dynamic activator = generic.Invoke(null, new object[] { ctor });
        return () => activator();
    }

    delegate T ObjectActivator<out T>(params object[] args);
}
Run Code Online (Sandbox Code Playgroud)

您可以使用它作为 的替代品Activator.CreateInstance,如果结果被缓存,它可以提供更高的性能。

var dictionary = Assembly.GetExecutingAssembly().GetTypes().
    Select(t => new { t, Attribute = t.GetCustomAttribute(typeof(MyEnumAttribute)) }).
    Where(e => e.Attribute != null).
    ToDictionary(e => (e.Attribute as MyEnumAttribute).Value, 
                 e => ConstructorFactory.Create<object>(e.t));
var wanted = MyEnum.Reset;
var instance = dictionary[wanted]();
Run Code Online (Sandbox Code Playgroud)