web-dev-qa-db-ja.com

文字列値でクラスを取得

いくつかの派生クラスを持つ抽象クラスがあります

public abstract class MyObject
{
    public string name { get; set; }
    public bool IsObject(string pattern);
    ...
}

public class MyObjectA : MyObject
{
    public string name { get { return "MyObjectA"; } set; }
    public bool IsObject(string pattern) { ... }
    ...
}

public class MyObjectB: MyObject
{
  public string name { get { return "MyObjectB"; } set; }
  public bool IsObject(string pattern) { ... }
  ...
}

次に、文字列に基づいて特定のクラス(MyObjectA/MyObectB)を返す関数が必要です。問題は、それを取得するためのif/else句がたくさんあることです。

public MyObject Create(string pattern)
{
    MyObjectA obj = new MyObjectA();
    if(obj.IsObject(pattern)
    {
        return obj;
    }
    else
    {
        MyObjectB objb = new MyObjectB();
        if(objb.IsObject(pattern);
            return objb;
        else
            ...
    }
}

それはひどいように見えます。これを行うためのより良い方法は何でしょうか?

21
Link

はい、Reflectionを使用してください。

Type.GetType を使用して、クラスのTypeのインスタンスを文字列で取得し、_Activator.CreateInstance_を使用してインスタンス化できます。

_public MyObject Create(string pattern)
{
    Type t = Type.GetType(pattern);
    if (t == null) {
        throw new Exception("Type " + pattern + " not found.");
    }
    return Activator.CreateInstance(t);
}
_

Activator.CreateInstance(string, string)オーバーロードを使用することもできますが、これは必要なTypeの新しいインスタンスを直接返すことはありません。

28
Rudi Visser

Reflectionまたは System.Activator.CreateInstance を使用して、TypeまたはTypeNameを文字列として基づいてインスタンスを作成できます。

7
dutzu

Rudi Visserが言ったように、 反射 を使用する必要があります。

また、クラス名を取得するには、ハードコーディングしないでください。 nameプロパティを使用したい場合は、次のように記述してください。

public abstract class MyObject
{
    public string name 
    { 
        get
        {
            return this.GetType().Name;
        }
    }
    public bool IsObject(string pattern);
    ...
}

クラスの名前がない場合は、それを表す文字列だけで、MyObjectから派生したすべてのクラスを確認できます。

public MyObject Create(string pattern)
{
    Type[] types = Assembly.GetExecutingAssembly().GetTypes();
    foreach (Type type in types.Where(t => t.IsSubclassOf(typeof(MyObject))))
    {
        MyObject obj = (MyObject)Activator.CreateInstance(type);
        if (obj.name == pattern)
        {
            return obj;
        }
    }
    throw new Exception("Type " + pattern + " not found.");
}
5

あなたが述べた質問には多くの良い答えがあります。ただし、このタイプの要件にはファクトリパターンを使用することをお勧めします。

'factory'は、静的GetNewMyObject(string pattern)メソッドとprotected static Dictionary<string, Type>を基本クラスに追加するのと同じくらい簡単です。次に、派生クラスは、パターン+タイプを(静的コンストラクターで)ファクトリに追加するだけで、基本クラスを変更せずに新しい派生クラスをファクトリに追加できます。

このように、「パターン」文字列はタイプ名と一致する必要はなく、次のように書くことができるように論理パターンマッチングを行う必要もありません。

public static MyObject GetNewMyObject(string pattern)
{
  return (MyObject)Activator.CreateInstance(StaticTypeDictionary[pattern]);
}
1
Andrew Hanlon

このようにReflectionを使用して行うことができます...

    public object getInstance(string assemblyName, string className, object[] constructorParameters)
    {
        System.Reflection.Assembly asm = System.Reflection.Assembly.Load(assemblyName);
        return asm.CreateInstance(className, false, System.Reflection.BindingFlags.CreateInstance, null, constructorParameters, null, null);
    }

assemblyName-フルパス+アセンブリ名

className-完全修飾クラス名

0
Kevin