views:

183

answers:

3

Ok, maybe that title didn't make much sense, but here is the deal. Say I have a generic method with multiple type constraints, this this:

public static void DoSomethingAwesome<T>(T thing)
    where T : IThing, IAwesome, IComparable<T>
{
    ...
}

Now.... how can I, using reflection, create something I can send in there?

If it was only one constraint I know I can do it like this:

var types = assembly
      .GetTypes()
      .Where(typeof (IThing).IsAssignableFrom)

foreach(var t in types)
    DoSomethingAwesome((IThing) Activator.CreateInstance(t));

But, can't really cast to multiple interfaces... how on earth can I solve this? You could say I am pretty much lost here now :P

Title got kind of long and complex as I wasn't sure what to call this, please improve if you can

A: 

I'm guessing there's some reason you can't do

var types = assembly
.GetTypes()
.Where(typeof (IThing).IsAssignableFrom && typeof (IAwesome).IsAssignableFrom))
JustLoren
Doesn't handle the IComparable<T> constraint, though, which is the trickiest.
Reed Copsey
To instantiate them, use Activator.CreateInstance as you do in the question. Then you have the separate problem of passing them to the method; I've posted an answer that discusses this.
itowlson
Well, I can, and will, of course do that, but that will just *find* those classes. I will still have trouble instantiating them.
Svish
Could you say what kind of trouble you are having instantiating them? As you know, Activator.CreateInstance should do that for you; if it isn't, what's the error you're seeing? Or is the problem in *casting* the created instance to a type that the compiler will let you pass to the generic method? If so see my answer for possible solutions.
itowlson
A: 

You need a type that's assignable from all of your constraints. The first two are easy, but the third is a bit trickier:

// Using
static bool IsIComparable(Type thing)
    {
        foreach (Type interfaceType in thing.GetInterfaces())
        {
            if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof (IComparable<>))
            {
                Type[] arguments = interfaceType.GetGenericArguments();
                if (arguments.Length == 1)
                {
                    if (arguments[0] == thing)
                        return true;
                }
            }
        }
        return false;
    }


// This returns an enumerable of compatible types:
var types = assembly.GetTypes().Where( t => 
   typeof(IThing).IsAssignableFrom(t) &&
   typeof(IAwesome).IsAssignableFrom(t) &&
   IsIComparable(t) );
Reed Copsey
This, as far as I can see, also just help me find those types. Not instantiating them in a way I can send them into a generic method. The `IsComparable` method looks very helpful though, so thank you for that one :)
Svish
+4  A: 

To add to Reed and Loren's answers about finding suitable types, note that you still won't be able to call DoSomethingAwesome by casting, because as you have found, the compiler doesn't provide a way to cast the instantiated object to multiple interfaces. You have two options:

  1. Create a new interface IAwesomeComparableThing which derives from IThing, IAwesome and IComparable<T>, have your types implement that, and cast to that.

  2. Invoke DoSomethingAwesome through reflection. To do this, you will need to get the MethodInfo for the DoSomethingAwesome generic method, then call MethodInfo.MakeGenericMethod with your type that implements all three interfaces.

Example of (2):

Type type = sometype; // For example found using reeds method
MethodInfo mgeneric = typeof(Awesomeiser).GetMethod("DoSomethingAwesome");
MethodInfo mspecific = mgeneric.MakeGenericMethod(new [] { type });
mspecific.Invoke(null, new [] { type });
itowlson
type I got from Reed?
Svish
Sorry, I didn't want to replicate Reed and Loren's answers about how to locate suitable types. I am assuming that, using the other answers, you can locate a suitable type and instantiate an instance (using Activator.CreateInstance). You then pass this type to MakeGenericMethod, and the instance to Invoke. Sorry for lack of clarity.
itowlson
Aah, think I got it now. But wat is m? should it be mgeneric?
Svish
Awesome. It worked :D (Fixed the code so it is correct)
Svish