Вопрос по reflection, generics, c#, activator – Как динамически создать универсальный объект C # с помощью отражения? [Дубликат]

115

This question already has an answer here:

Pass An Instantiated System.Type as a Type Parameter for a Generic Class 5 answers

В C # у меня есть следующий объект:

public class Item
{ }

public class Task<T>
{ }

public class TaskA<T> : Task<T>
{ }

public class TaskB<T> : Task<T>
{ }

Я хочу динамически создавать TaskA или TaskB с помощью отражения C # (Activator.CreateInstance). Однако я бы не знал тип заранее, поэтому мне нужно динамически создавать TaskA на основе строки типа & quot; namespace.TaskA & quot; или "namespace.TaskAB".

Ваш Ответ

5   ответов
2

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

Task<Item> itsMe = o as Task<Item>;

Или я что-то упустил?

Вы ничего не упускаете. Это я, кто не думал прямо. Я не должен был пить столько алкоголя в последний вечер! Jeff
7

На самом деле вы не сможете написать последнюю строку.

Но вы, вероятно, не хотите создавать объект, просто ради или создавая его. Вы, вероятно, хотите вызвать какой-то метод для только что созданного вами экземпляра.

Затем вам понадобится что-то вроде интерфейса:

public interface ITask 
{
    void Process(object o);
}

public class Task<T> : ITask
{ 
   void ITask.Process(object o) 
   {
      if(o is T) // Just to be sure, and maybe throw an exception
        Process(o as T);
   }

   public void Process(T o) { }
}

и назовите это с:

Type d1 = Type.GetType("TaskA"); //or "TaskB"
Type[] typeArgs = { typeof(Item) };
Type makeme = d1.MakeGenericType(typeArgs);
ITask task = Activator.CreateInstance(makeme) as ITask;

// This can be Item, or any type derived from Item
task.Process(new Item());

В любом случае вы не будете статически приводиться к типу, о котором вы не знали заранее (в данном случае «makeme»). ITask позволяет вам добраться до вашего целевого типа.

Если это не то, чего вы хотите, вам, вероятно, нужно быть более конкретным в том, что вы пытаетесь достичь с помощью этого.

У меня есть что-то вроде Process () в Task & lt; T & gt ;. И в моем случае я на самом деле больше не беспокоюсь о последней строке, поскольку вы заявили, что я просто вызываю task.Process (), поэтому возможность кодирования последней строки становится неуважительной. Jeff
1

Я знаю, что этот вопрос решен, но в интересах всех, кто его читает; если у вас есть все типы, задействованные в виде строк, вы можете сделать это как один слой:

IYourInterface o = (Activator.CreateInstance(Type.GetType("Namespace.TaskA`1[OtherNamespace.TypeParam]") as IYourInterface);

Всякий раз, когда я делал подобные вещи, у меня был интерфейс, который я хотел использовать в последующем коде, поэтому я приводил созданный экземпляр к интерфейсу.

1

Убедитесь, что вы делаете это по уважительной причине. Простая функция, подобная следующей, разрешает статическую типизацию и позволяет вашей IDE выполнять такие действия, как & quot; Поиск ссылок & quot; и Refactor - & gt; Переименовать.

public Task <T> factory (String name)
{
  Task <T> result;

  if (name.CompareTo ("A") == 0)
  {
    result = new TaskA ();
  }
  else if (name.CompareTo ("B") == 0)
  {
    result = new TaskB ();
  }

  return result;
}
Почему вы используете .CompareTo? Почему бы не == или .Equals (если вы хотите больше контроля). Может быть, переключатель будет даже лучше.
Я никогда не проверял, выполняет ли C # сравнение строк на == вместо сравнения ссылок. CompareTo и Equals должны иметь одинаковую эффективность времени выполнения, если они были реализованы правильно. Блок переключателей не будет иметь таких же ускорений, как и помещение целого числа в блок переключателей; он будет компилироваться в блок if-else.
218

Проверьте этостатья и этопростой пример, Быстрый перевод же на ваши занятия ...

var d1 = typeof(Task<>);
Type[] typeArgs = { typeof(Item) };
var makeme = d1.MakeGenericType(typeArgs);
object o = Activator.CreateInstance(makeme);

Для вашего редактирования: для этого случая вы можете сделать это ...

var d1 = Type.GetType("GenericTest.TaskA`1"); // GenericTest was my namespace, add yours
Type[] typeArgs = { typeof(Item) };
var makeme = d1.MakeGenericType(typeArgs);
object o = Activator.CreateInstance(makeme);

Чтобы узнать, где я нашел backtick1 для имени универсального класса, см.Эта статья.

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

Type type = typeof(IReadOnlyDictionary<,>);
Требуется ли обратный удар, то есть, если он опущен, компилятор предполагает, что он равен 1?
Вы также можете использовать Activator.CreateInstance & lt; T & gt; что может исключить необходимость создавать указание «typeargs» и сделайте общий тип самостоятельно.
как сравнить общий тип, используяis ключевое слово?
Спасибо за ссылку на мою статью в блоге "Использование отражения для создания общего класса в C # .Net" (omegacoder.com/?p=38) в качестве «простого примера». :-) Я рад, что статья обретает новую жизнь.

Похожие вопросы