2009-08-15 2 views
4

Предположим, у меня есть базовый класс FooParent, и он имеет множество FooChildren. Во время выполнения я должен создать экземпляр одного из FooChildren. Как мне это сделать? Я понимаю, что могу создать огромную карту (и использовать делегатов) или огромный оператор switch/case, но это кажется немного неряшливым. В чем-то вроде PHP, я могу легко создать класс динамически, как это:Инициализация динамического класса в .NET

$className="FooClass"; 
$myNewFooClass=new $className; //makes a new instance of FooClass 

(вы также можете сделать это с помощью отражения).

Есть ли у .NET что-то вроде этого? Является ли отражение опцией, и есть ли у нее какие-либо штрафы за производительность? Если нет, какие у меня есть другие варианты?

Тип класса будет определяться запросом JSON. Переменной может быть все, что я хочу ... это может быть целое число, если я хотел сделать перечисление, или это может быть полное имя класса. Я еще не создал его, поэтому я не определился.

+0

Посмотрите «Activator.CreateInstance». –

+0

Будет ли класс всегда иметь конструктор без параметров? –

ответ

8

Вы можете сделать это с отражением, если хотите, но будут штрафы за производительность. Являются ли они значительными или нет, будут зависеть от вашей точной ситуации.

В зависимости от того, что вы хотите сделать, я бы вполне мог пойти с оператором switch/case или с картой, как вы предлагаете. В частности, это было бы полезно, если бы вам нужно было передать разные аргументы различным конструкторам на основе типа, который вы строите, - делая это через отражение, было бы немного болью в том, что вы уже были бы специальными корпусами разных типов.


EDIT: Хорошо, теперь мы знаем, что всегда будет конструктор без параметров. В этом случае ваш JSON легко может содержать имя класса без имен (если они все в том же пространстве имен), и ваш метод может выглядеть следующим образом:

public FooParent CreateFoo(string name) 
{ 
    if (name == null) 
    { 
     throw new ArgumentNullException("name"); 
    } 
    string fullName = "Some.NameSpace." + name; 
    // This is assuming that the type will be in the same assembly 
    // as the call. If that's not the case, we can look at that later. 
    Type type = Type.GetType(fullName); 
    if (type == null) 
    { 
     throw new ArgumentException("No such type: " + type); 
    } 
    if (!typeof(FooParent).IsAssignableFrom(type)) 
    { 
     throw new ArgumentException("Type " + type + 
            " is not compatible with FooParent."); 
    } 
    return (FooParent) Activator.CreateInstance(type); 
} 

Где вы определяете имя для использования? Если это прошло в где-то, переключатель заявление может быть очень простым, когда переформатирован от нормы немного:

public FooParent CreateFoo(string name) 
{ 
    switch (name) 
    { 
     case "Foo1":  return new Foo1(); 
     case "Foo2":  return new Foo2(); 
     case "Foo3":  return new Foo3(); 
     case "Foo4":  return new Foo4(); 
     case "FooChild1": return new FooChild1(); 
     default: 
      throw new ArgumentException("Unknown Foo class: " + name); 
    } 
} 

Имейте в виду, только что написано, что из Я не уверен, что это имеет какое-либо реальной пользы (кроме) с использованием Type.GetType(name), а затем Activator.CreateInstance(type).

Каким образом вызывающий абонент узнает имя класса? Будет ли это определенно динамичным? Есть ли вероятность, что вы можете использовать дженерики? Чем больше вы могли бы рассказать нам о ситуации, тем полезнее было бы.

+0

Я обновил свое сообщение, надеюсь, будет немного более ясно. – ryeguy

+0

Да, у класса всегда будет безпараметрический конструктор. – ryeguy

3

До тех пор, пока у всех ваших FooChildren нет конструкторов без параметров, вы можете сделать это с помощью отражения.

Activator.CreateInstance<FooChildType>(); 

Если вы на самом деле не имеют ссылку на тип, и все у вас есть строка с именем класса, вы можете сделать:

Activator.CreateInstance("FooChildClassName", "Fully.Qualified.AssemblyName"); 

Существует штраф производительности с отражением, но я бы не стал зависеть от него, если это окажется для вас самым простым решением, и ваша эффективность приемлема.

0

Если вам нужна производительность, есть еще один вариант. Используйте

Type yourType = Type.GetType("FooClass"); 

Чтобы узнать тип вашего класса. Теперь вы можете использовать

ConstructorInfo ctor = yourType.GetConstructor(new Type[0]); 

Чтобы получить информацию о конструкторе для вашего конструктора. Если вам не нужен конструктор по умолчанию, передайте массив типов, которые вы хотите передать конструктору. Теперь вы можете использовать конструктор следующим образом:

object instanceOfFooClass = ctor.Invoke(new object[0]); 

Первые два шага должны выполняться только один раз. Вы можете сохранить «ctor» для дальнейшего использования. Это должно быть быстрее, если вызвать Activator.CreateInstance.

Смежные вопросы