7

I got a custom class, who relies on a generic type T to be passed along. I only know what type it is in string form, because that's how it's being sent. I've been searching around but can't seem to find exactly what I need. I can parse the string value to a type, but I need to parse it to... something, that I can pass as a generic parameter.


I've rewritten my problem, as such:

// Classes structure
namespace Mynamespace
{
    public interface IRequest
    {
    }

    public interface IHandler<T> where T : IRequest
    {
        void Handle(T item);
    }

    public class MyRequest : IRequest
    {
    }

    public class MyHandler : IHandler<MyRequest>
    {
        void Handle(MyRequest item)
        {
        }
    }
}

// The info I get, and I know typeString is a IRequest
string typeString = "My";
object requestItem = [insert xml parsing here];

// I then create a handler, to handle the request
Type typeHandler = Type.GetType("Mynamespace." + typeString + "Handler");
var handler = Activator.CreateInstance(typeHandler);

Type typeRequest = Type.GetType("Mynamespace." + typeString + "Request");

// what I want to do:
handler.Handle(requestItem);

I can't do that because handler and requestItem are just objects So I need to parse 'handler' to 'typeHandler', and requestItem to 'typeRequest'

Edit: I figured it out, I used InvokeMember to access it. :)

typeHandler.InvokeMember("Handle", BindingFlags.InvokeMethod, null, handler, new[] { requestItem });
Rik De Peuter
  • 703
  • 2
  • 9
  • 24

3 Answers3

10

You need Type.MakeGenericType:

Type typeArgument = Type.GetType(string.Format("Mynamespace.{0}", typeString));
Type template = typeof(MyClass<>);

Type genericType = template.MakeGenericType(typeArgument);

object instance = Activator.CreateInstance(genericType);

Note that you can't cast this to a particular MyClass<T> because you don't know T - but it will be an instance of the right class at execution time.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • +1. Also note that, although you can't cast the result to a particular `MyClass`, you may want to define a non-generic interface (e.g. `IMyClass`) and cast to that. – Daniel Pryden Sep 16 '11 at 22:25
  • @Daniel: Indeed. I thought I'd hold back from that until the OP gave more information :) – Jon Skeet Sep 16 '11 at 22:25
  • This is what I sort of do as well already, to create a new instance. But that returns an object, and that object, I need to parse to a MyClass. Because the 'MyClass' has a void that requires the T. – Rik De Peuter Sep 16 '11 at 22:32
  • @Daniel: In my real code I use an interface, and an extra class in between, but I tried here to make a sample of what my problem is, without making it too complicated. :) – Rik De Peuter Sep 16 '11 at 22:32
  • @FrieK: What do you mean, "has a void that requires the T"? You aren't going to be able to use **compile-time** generic dispatch based on a type that is only determined at **run time** -- it's simply not possible. Perhaps `dynamic` will do what you want, but I don't understand your problem well enough to be sure. – Daniel Pryden Sep 16 '11 at 22:45
  • Hmm, I think it's the impossible one that I want, but I am rewriting my question to make it more obvious, I hope. – Rik De Peuter Sep 16 '11 at 22:48
  • Hi Daniel, I've rewritten my problem, maybe it will make more sense like that. :) Thanks again! – Rik De Peuter Sep 16 '11 at 23:04
4
Type closedType = typeof(MyClass<>).MakeGenericType(myGeneric);
object obj = Activator.CreateInstance(closedType);

Note that unless you have a non-generic interface or base-type, it is very tricky to talk to this type of object (unless you cheat by using dynamic). For example, a non-generic interface can be helpful:

var obj = (ISomeInterface)Activator.CreateInstance(closedType);
obj.SomeMethodOnTheNonGenericInterface();
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
1

I figured it out, I used InvokeMember to access it. :)

typeHandler.InvokeMember("Handle", BindingFlags.InvokeMethod, null, handler, new[] { requestItem });
Rik De Peuter
  • 703
  • 2
  • 9
  • 24