我有一个对象,如果它实现特定类型的接口,我想调用某些方法。
我正在检查下面的通用接口。
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
}
我不知道如何转换对象,使其成为调用doSomethingWithINotifyObject
的正确类型
这个例子中的方法看起来像
private void doSomethingWithINotifyObject<T>(INotify<T> notify) where T : EventArgs, INotificationArgs
{
//do stuff with notification object
}
INotify接口定义为
public interface INotify<T> where T: EventArgs, INotificationArgs
{
//notify stuff
}
有没有什么方法可以将我的对象强制转换为INotify<T> where T : EventArgs, INotificationArgs
,而不关心T实际上是什么?
我试着做了一个通用的方法,比如下面的
typeof(MyClass)
.GetMethod("doSomethingWithINotifyObject")
.MakeGenericMethod(notifyObject.GetType())
.Invoke(this, new object[] { notifyObject });
我得到以下运行时异常
运行时异常(第13行(:"Void doSomethingWithINotifyObjectT"上的GenericArguments[0],"MyClass+doSometingWithINotiFYObject"违反了类型"T"的约束。
堆栈跟踪:
[System.Security.VerificationException:方法MyClass.doSomethingWithINotifyObject:类型参数"MyClass+doSomethingWithINotifyObject"违反了类型参数"T"的约束。]位于System.RuntimeMethodHandle.GetStubIfNeeded(RuntimeMethodhandlInternal方法、RuntimeType declaringType、RuntimeType[]methodInstantation(在System.Reflection.RuntimeMethodInfo.MakeGenericMethod(Type[]methodInstantation(
[System.ArgumentException:"Void doSomethingWithINotifyObjectT"上的GenericArguments[0],"MyClass+doSometingWithINotify Object"违反了类型为"T"的约束。]位于System.RuntimeType.ValideGenericArguments(MemberInfo定义,RuntimeType[]generalArguments,Exception e(位于System.Reflection.RuntimeMethodInfo.MakeGenericMethod(Type[]methodInstantation(在MyClass.Main((:第13行
我在.NET fiddle上有一个这个场景的例子https://dotnetfiddle.net/ZWMJ4x
typeof(MyClass)
.GetMethod("doSomethingWithINotifyObject")
.MakeGenericMethod(notify.GetType())
.Invoke(null, new object[] { notify });
不起作用,因为Notify
类不是泛型的,即使它确实实现了泛型INotify<FooBarClass>
。需要传递给MakeGenericMethod
的是Notify
:使用的实际类型参数
typeof(MyClass)
.GetMethod("doSomethingWithINotifyObject")
.MakeGenericMethod(iFace.GetGenericArguments())
.Invoke(null, new object[] { notify });
不,你需要知道T
是什么,否则类型系统无法检查你的参数是否是正确的类型,或者你的返回值类型是否正确,等等。
你可以通过反射来做任何事情,并绕过所有这些,或者如果你真的不在乎T
是什么,只需将这些方法放在一个通用接口中并转换为它(它们毕竟不应该有Ts(。
如果INotify
相对于T.是协变的,则可能没有反射
由于您有一个由两部分组成的类型约束,因此需要定义一个实现这两个约束的基类,并从中派生INotify<>
类。
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);
}
}
}
}
输出:
1
2
3
链接到DotNetFiddle示例