使用where子句

时间:2018-08-02 21:07:33

标签: c# generics reflection

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

我正在检查以下通用接口。

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行):GenericArguments [0],“ Void doSomethingWithINotifyObjectT”上的“ MyClass + doSomethingWithINotifyObject”违反了类型“ T”的约束。

     

堆栈跟踪:

     

[System.Security.VerificationException:方法MyClass.doSomethingWithINotifyObject:类型参数'MyClass + doSomethingWithINotifyObject'违反了类型参数'T'的约束。      在System.RuntimeMethodHandle.GetStubIfNeeded(RuntimeMethodHandleInternal方法,RuntimeType声明类型,RuntimeType [] methodInstantiation)      在System.Reflection.RuntimeMethodInfo.MakeGenericMethod(Type [] methodInstantiation)

     

[System.ArgumentException:GenericArguments [0],“ Void doSomethingWithINotifyObjectT”上的“ MyClass + doSomethingWithINotifyObject”违反了类型“ T”的约束。]      在System.RuntimeType.ValidateGenericArguments处(MemberInfo定义,RuntimeType [] genericArguments,异常e)      在System.Reflection.RuntimeMethodInfo.MakeGenericMethod(Type [] methodInstantiation)      在MyClass.Main()处:第13行

我在.NET上有一个这种情况的例子,这里https://dotnetfiddle.net/ZWMJ4x

3 个答案:

答案 0 :(得分:1)

否,您需要知道T是什么,因为否则类型系统将无法检查您的参数是否正确,或者返回值类型是否正确,等等。

您可以通过反射来做所有事情,并绕过所有这些,或者如果您真的不在乎T只是将这些方法放在通用接口中并强制转换为该接口(毕竟不应该包含T。

答案 1 :(得分:1)

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 });

答案 2 :(得分:0)

如果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

Link to DotNetFiddle example