Подтвердить что ты не робот

Получить класс по строковому значению

У меня есть абстрактный класс с несколькими производными классами

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-clauses, чтобы получить это:

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

Это выглядит просто ужасно. Каким будет лучший способ сделать это?

4b9b3361

Ответ 1

Да, используйте 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.

Ответ 2

Вы можете использовать Reflection или System.Activator.CreateInstance, чтобы создать экземпляр, основанный на типе Type или TypeName как строку.

Ответ 3

Как сказал Руди Виссер, вы должны использовать отражение.

Также, чтобы получить имя вашего класса, вы не должны его жестко кодировать. Если вы хотите использовать свойство 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.");
}

Ответ 4

Есть много хороших ответов на ваш заявленный вопрос. Однако я бы предложил использовать шаблон factory для этого типа требований.

Ваш 'factory' может быть таким же простым, как добавление статического метода GetNewMyObject(string pattern) к вашему базовому классу, а также к protected static Dictionary<string, Type>. Затем ваши производные классы могли просто добавить свой шаблон + к factory (в статическом конструкторе), позволяя добавлять новые производные классы в factory без изменения базового класса.

Таким образом, строки вашего шаблона не должны совпадать с именем типа, а также вам не нужно делать какие-либо логические соответствия шаблонов, которые вы можете написать:

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

Ответ 5

Это можно сделать, используя 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 - полное имя класса