How to dynamically create generic C# object using reflection? [duplicate]
Asked Answered
T

5

156

In C# I have the following object:

public class Item
{ }

public class Task<T>
{ }

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

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

I want to dynamically create TaskA or TaskB using C# reflection (Activator.CreateInstance). However I wouldn't know the type before hand, so I need to dynamically create TaskA based on string like "namespace.TaskA" or "namespace.TaskAB".

Transistor answered 20/7, 2009 at 1:58 Comment(0)
S
283

Check out this article and this simple example. Quick translation of same to your classes ...

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

Per your edit: For that case, you can do this ...

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);

To see where I came up with backtick1 for the name of the generic class, see this article.

Note: if your generic class accepts multiple types, you must include the commas when you omit the type names, for example:

Type type = typeof(IReadOnlyDictionary<,>);
Sela answered 20/7, 2009 at 2:2 Comment(5)
Is the backtick required, i.e. if it is ommited, does the compiler assume it is 1?Mathi
Thanks for linking to my blog article "Using Reflection to Instantiate a Generic Class in C# .Net"(omegacoder.com/?p=38) as the "Simple Example". :-) I am glad the article is finding new life.Vapory
how do I compare generic type using is keyword?Gayl
You can also use Activator.CreateInstance<T> which may eliminate the need to create specify the "typeargs" and make the generic type yourself.Contrapuntal
Francis Dean, the point is you have Type object and you cannot place is in the <>Skycap
H
9

Indeed you would not be able to write the last line.

But you probably don't want to create the object, just for the sake or creating it. You probably want to call some method on your newly created instance.

You'll then need something like an interface :

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) { }
}

and call it with :

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());

In any case, you won't be statically cast to a type you don't know beforehand ("makeme" in this case). ITask allows you to get to your target type.

If this is not what you want, you'll probably need to be a bit more specific in what you are trying to achieve with this.

Hypno answered 20/7, 2009 at 2:49 Comment(1)
I do have something like Process() in Task<T>. And in my case I actually don't care about the last line anymore as you stated I am simply calling task.Process() therefore whether is able to code the last line becomes irreverent.Transistor
T
3

It seems to me the last line of your example code should simply be:

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

Or am I missing something?

Tottering answered 20/7, 2009 at 2:35 Comment(1)
You are not missing anything. It's me who wasn’t thinking straight. I shouldn’t have drunk that much alcohol last nite!Transistor
L
1

Make sure you're doing this for a good reason, a simple function like the following would allow static typing and allows your IDE to do things like "Find References" and Refactor -> Rename.

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;
}
Lodhia answered 21/7, 2009 at 22:46 Comment(2)
Why are you using .CompareTo? Why not == or .Equals (if you want more control). Maybe a switch would even be better.Exsiccate
I never checked if C# does a string comparison on == instead of a reference comparison. CompareTo and Equals should have the same run time efficiency if they were implemented correctly. A switch block wouldn't have the same speed-ups as putting an integer in a switch block; it would compile to an if-else block.Lodhia
P
1

I know this question is resolved but, for the benefit of anyone else reading it; if you have all of the types involved as strings, you could do this as a one liner:

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

Whenever I've done this kind of thing, I've had an interface which I wanted subsequent code to utilise, so I've casted the created instance to an interface.

Paraphrase answered 24/10, 2013 at 10:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.