0

I have an object that I want to call certain methods if it implements specific kinds of interfaces.

I am checking for the generic interfaces like below.

foreach (var iFace in objectType.GetInterfaces())
{
    if (iFace.IsGenericType && iFace.GetGenericTypeDefinition() == typeof(INotify<>))
    {
        //fails to compile because object isn't the correct type.
        doSomethingWithINotifyObject(notifyObject)
    }
    //do other things if different interfaces
}

What I don't know how to do is cast the object so that it is the correct type to call doSomethingWithINotifyObject

The method in this example looks something like this

private void doSomethingWithINotifyObject<T>(INotify<T> notify) where T : EventArgs, INotificationArgs
{
    //do stuff with notification object
}

The INotify interface is defined as

public interface INotify<T> where T: EventArgs, INotificationArgs
{
    //notify stuff
}

Is there any way to cast my object to an INotify<T> where T : EventArgs, INotificationArgs and not care what T actually is?

I tried making a generic method like the following

typeof(MyClass)
    .GetMethod("doSomethingWithINotifyObject")
    .MakeGenericMethod(notifyObject.GetType())
    .Invoke(this, new object[] { notifyObject });

And I get the following runtime exception

Run-time exception (line 13): GenericArguments[0], 'MyClass+doSomethingWithINotifyObject', on 'Void doSomethingWithINotifyObjectT' violates the constraint of type 'T'.

Stack Trace:

[System.Security.VerificationException: Method MyClass.doSomethingWithINotifyObject: type argument 'MyClass+doSomethingWithINotifyObject' violates the constraint of type parameter 'T'.] at System.RuntimeMethodHandle.GetStubIfNeeded(RuntimeMethodHandleInternal method, RuntimeType declaringType, RuntimeType[] methodInstantiation) at System.Reflection.RuntimeMethodInfo.MakeGenericMethod(Type[] methodInstantiation)

[System.ArgumentException: GenericArguments[0], 'MyClass+doSomethingWithINotifyObject', on 'Void doSomethingWithINotifyObjectT' violates the constraint of type 'T'.] at System.RuntimeType.ValidateGenericArguments(MemberInfo definition, RuntimeType[] genericArguments, Exception e) at System.Reflection.RuntimeMethodInfo.MakeGenericMethod(Type[] methodInstantiation) at MyClass.Main() :line 13

I have an example of this scenario on .NET fiddle here https://dotnetfiddle.net/ZWMJ4x

TJ Rockefeller
  • 3,178
  • 17
  • 43
  • 1
    Possible duplicate of [Using System.Type to call a generic method](https://stackoverflow.com/questions/14222277/using-system-type-to-call-a-generic-method) – Camilo Terevinto Aug 02 '18 at 21:14
  • Updated my question with the runtime error that I get when using the solution in the possible duplicate. – TJ Rockefeller Aug 02 '18 at 22:24
  • If you can edit your question to show an actual [mcve] I might be able to help (your variable names don't match so it's hard to follow) – Camilo Terevinto Aug 02 '18 at 22:36
  • @CamiloTerevinto I've edited my answer to show why it is not a duplicate. I used the solution from the linked question and got a runtime error. I linked a .net fiddle with an example of how that runtime error is being thrown. – TJ Rockefeller Aug 03 '18 at 13:40

3 Answers3

1

No, you need to know what T is because otherwise the type system can't check if your parameters are the right type, or if your return value types are correct, etc.

You could do everything via reflection and bypass all that, or if you really don't care what T is just put those methods in a generic interface and cast to that (they shouldn't have Ts in them after all).

BradleyDotNET
  • 60,462
  • 10
  • 96
  • 117
1
typeof(MyClass)
    .GetMethod("doSomethingWithINotifyObject")
    .MakeGenericMethod(notify.GetType())
    .Invoke(null, new object[] { notify });

Doesn't work because your Notify class is not generic, even though it does implement the generic INotify<FooBarClass>. What you need to pass to MakeGenericMethod is the actual type arguments used by Notify:

typeof(MyClass)
    .GetMethod("doSomethingWithINotifyObject")
    .MakeGenericMethod(iFace.GetGenericArguments())
    .Invoke(null, new object[] { notify });
Camilo Terevinto
  • 31,141
  • 6
  • 88
  • 120
0

It's possible without Reflection if INotify is covariant with respect to T.

Since you have a two-part type constraint, you will need to define a base class that implements both, and derive your INotify<> classes from that.

public interface INotificationArgs
{
}

public interface INotify<out T> where T: EventArgs, INotificationArgs
{
    void PrintName();
}

public class MyEventArgsBase : EventArgs, INotificationArgs {}

public class MyEventArgs1 : MyEventArgsBase {}

public class MyEventArgs2 : MyEventArgsBase {}

public class MyEventArgs3 : MyEventArgsBase {}

class MyClass<T> : INotify<T> where T : EventArgs, INotificationArgs
{
    public string Name { get; set; }

    public MyClass(string name) { Name = name; }

    public void PrintName()
    {
        Console.WriteLine(Name);
    }
}

public class Program
{

    private static void doSomethingWithINotifyObject<T>(INotify<T> notify) where T : EventArgs, INotificationArgs
    {
        notify.PrintName();
    }

    public static void Main()
    {
        object[] tests = new object []
        {
            new MyClass<MyEventArgs1>("1"),
            new MyClass<MyEventArgs2>("2"),
            new MyClass<MyEventArgs3>("3")
        };

        foreach (var o in tests)
        {
            var i = o as INotify<MyEventArgsBase>;
            if (i != null)
            {
                doSomethingWithINotifyObject(i);
            }
        }
    }
}

Output:

1
2
3

Link to DotNetFiddle example

John Wu
  • 50,556
  • 8
  • 44
  • 80
  • I'm not seeing how this addresses the fact that in my example my where clause is `where T : EventArgs, INotificationArgs` My generic method is using specific properties based on the fact that it implements INotificationArgs. – TJ Rockefeller Aug 02 '18 at 21:41
  • Updated the example to illustrate. – John Wu Aug 02 '18 at 21:54
  • By the way, I didn't down vote your answer. I think I understand what you are suggesting, and I will see if it will work in my situation. – TJ Rockefeller Aug 02 '18 at 22:02
  • I think this will work for my situation. Basically I just need T to implement an abstract base class instead of implementing an abstract base class and an interface so that I can cast to `INotify`. I will see if there are any more answers that are more direct before marking this as the excepted solution. I do like that this doesn't use reflection which is nice, but it does require that you maintain all of the code that you are using and can make these changes. – TJ Rockefeller Aug 02 '18 at 22:22