まず、属性を宣言してクラスに追加します。
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
から適応したクラスを追加しています。
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]();