gpt4 book ai didi

c# - 如何在 C# 中使用自定义属性来替换开关中的开关?

转载 作者:太空狗 更新时间:2023-10-29 23:00:41 24 4
gpt4 key购买 nike

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

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

但是,我对属性很陌生,在网上没有找到任何合适的解决方案。如果有人能给我一些提示或几行代码,我将不胜感激!

最佳答案

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

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
{
}

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

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

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

编辑:我正在添加 ConstructorFactory类,改编自 this页面。

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

您可以将其用作 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]();

关于c# - 如何在 C# 中使用自定义属性来替换开关中的开关?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14139389/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com