Reputation: 3732
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
Upvotes: 0
Views: 1177
Reputation: 32068
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 });
Upvotes: 1
Reputation: 52250
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
Upvotes: 0
Reputation: 61349
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).
Upvotes: 1