Как динамически создавать общий объект С# с использованием отражения?
В С# у меня есть следующий объект:
public class Item
{ }
public class Task<T>
{ }
public class TaskA<T> : Task<T>
{ }
public class TaskB<T> : Task<T>
{ }
Я хочу динамически создавать TaskA или TaskB с использованием рефлекса С# (Activator.CreateInstance). Однако я бы не знал тип перед рукой, поэтому мне нужно динамически создавать TaskA на основе строки типа "namespace.TaskA" или "namespace.TaskAB".
Ответы
Ответ 1
Просмотрите статью и этот простой пример. Быстрый перевод того же самого в классы...
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<,>);
Ответ 2
Действительно, вы не сможете написать последнюю строку.
Но вы, вероятно, не хотите создавать объект, просто ради его создания или создания. Вероятно, вы захотите вызвать какой-то метод для своего вновь созданного экземпляра.
Вам понадобится что-то вроде интерфейса:
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 позволяет перейти к типу цели.
Если это не то, что вы хотите, вы, вероятно, должны быть немного более конкретными в том, что вы пытаетесь достичь с этим.
Ответ 3
Мне кажется, что последняя строка вашего примера кода должна быть следующей:
Task<Item> itsMe = o as Task<Item>;
Или я что-то не хватает?
Ответ 4
Удостоверьтесь, что вы делаете это по уважительной причине, простая функция, подобная приведенной ниже, позволит статическую типизацию и позволяет вашей среде IDE делать такие вещи, как "Найти ссылки" и "Рефакторинг" → "Переименовать".
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;
}
Ответ 5
Я знаю, что этот вопрос разрешен, но в интересах кого-то еще его читающего; если у вас есть все типы, используемые в качестве строк, вы можете сделать это как один лайнер:
IYourInterface o = (Activator.CreateInstance(Type.GetType("Namespace.TaskA`1[OtherNamespace.TypeParam]") as IYourInterface);
Всякий раз, когда я это делал, у меня был интерфейс, который я хотел использовать для последующего использования кода, поэтому я применил созданный экземпляр к интерфейсу.