使用where子句强制转换为泛型接口



我有一个对象,如果它实现特定类型的接口,我想调用某些方法。

我正在检查下面的通用接口。

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示例

最新更新