无约束类型参数铸造

Ger*_*ino 6 .net c# types casting

我的情况:

interface ISomeInterface {
    void DoSmth<T>(T other);
}

class Base : ISomeInterface
{
    public virtual void DoSmth<T>(T other){
        // for example do nothing
    }
}

class Derived<T2> : Base {
    Action<T2> MyAction {get;set;}

    public override void DoSmth<T>(T other){
        if(typeof(T2).IsAssignableFrom(typeof(T)))
            MyAction((T2) other);
    }
}
Run Code Online (Sandbox Code Playgroud)

这给了我一个错误:( Cannot cast expression of type 'T' to type 'T2'Cannot convert type 'T' to 'T2')

我明白这是因为T或T2都没有受到限制class,但如果我知道 - 由于IsAssignableFrom- 我可以使用T我需要T2,我怎么能说服编译器允许它?

que*_*atl 3

编译器会看到 T2 和 T 标识符,并帮助您通知您这些类型似乎不相关。这是绝对正确的,因为它们没有关系:没有通用约束可以断言它们之间的任何关系(不过我并不是说这在这里有用:))。

这是否是一个好功能,是有争议的。有时我希望它只是一个警告,但在大多数情况下,将其作为编译错误会很有帮助。泛型类中此类转换的数量远低于“正常”代码中的拼写错误数量:)

解决方案非常非常简单。只要欺骗编译器使其对源类型一无所知,它就会跳过针对目标类型的类型层次结构检查:

T theTee = ...;
object temp = (object)theTee;
T2 teeTwo = (T2)temp; // ok! 'temp' is now seen as 'object'
Run Code Online (Sandbox Code Playgroud)

或在一个衬垫中:

T2 teeTwo = (T2)(object)theTee;
Run Code Online (Sandbox Code Playgroud)

同样,您可以使用dynamic(如果您的 .Net 版本可用):

T theTee = ...;
dynamic temp = theTee;
T2 teeTwo = temp;
Run Code Online (Sandbox Code Playgroud)

和 oneliner 也应该工作:

T2 teeTwo = (dynamic)theTee;
Run Code Online (Sandbox Code Playgroud)

尽管我从未尝试过那个,因为我认为它比通过对象投射更重。不过,我还没有检查对象/动态方法的性能。只是一种预感。