5

我正在学习“逆变通用委托”。

我的理解是:

“in”关键字指定类型参数是逆变的。
这允许隐式转换委托类型。

如果没有“in”关键字,我们不知道类型参数是否是逆变的。
那么委托类型的隐式转换是不允许的。

这是我的代码:

public class Test
{
    //public delegate bool FuncDelegate<T>(T t);
    public delegate bool FuncDelegate<in T>(T t);

    public class BaseClass
    {
        public int x;
    }

    public class DerivedClass: BaseClass
    {
        public int y;
    }

    static bool BaseFunc(BaseClass bc)
    {
        if (bc.x > 1)
            return false;
        else
            return true;
    }

    static bool DerivedFunc(DerivedClass dc)
    {
        if (dc.y > 1)
            return false;
        else
            return true;
    }

    public static void Main()
    {
        FuncDelegate<DerivedClass> genericDerivedFunc = DerivedFunc;
        FuncDelegate<BaseClass> genericBaseFunc = BaseFunc;

        genericDerivedFunc = genericBaseFunc;

        FuncDelegate<DerivedClass> genericDerivedFunc2 = BaseFunc;
    }
}

我的问题

/*
    This line is valid when declared as: public delegate bool FuncDelegate<in T>(T t);
    This line is invalid when declared as: public delegate bool FuncDelegate<T>(T t);
*/
genericDerivedFunc = genericBaseFunc;

这条线与我的理解一致。

/*
    This line is always valid.
*/
FuncDelegate<DerivedClass> genericDerivedFunc2 = BaseFunc;

我不明白这一行:

"bool BaseFunc(BaseClass bc)" 可以隐式转换为 bool "FuncDelegate<DerivedClass>(DerivedClass t)"。

我认为它必须有“in”关键字来指定逆变。

但是可以在没有“in”关键字的情况下完成转换。

4

2 回答 2

5

注意这两个赋值右侧的区别:

genericDerivedFunc = genericBaseFunc;
genericDerivedFunc2 = BaseFunc;

第一行的右侧是委托,因此您将委托类型转换为另一种委托类型。这需要一个方差转换,如 C# 规范中的可用转换中所列:

隐式引用转换是:

  • ...
  • 从任何 reference_type 到接口或委托类型 T,如果它具有到接口或委托类型 T0 的隐式标识或引用转换,并且 T0 可方差转换为 T。

方差转换需要那些ins 和outs。

但是在第二行,右侧是方法组(方法的名称),所以在第二行,您实际上是在进行方法组转换。要使此类转换可用,BaseFunc需要与目标委托类型兼容。请注意,这是对方法的要求,而不是对委托类型的要求。要“兼容”。

M值得注意的是,方法与委托类型“兼容”的两个要求D是:

  • 对于每个值参数,存在从参数类型 inD到对应参数类型 in的恒等转换或隐式引用转换M
  • M从 的返回类型到 的返回类型存在标识或隐式引用转换D

这些要求使它看起来好像委托类型的in所有参数和out返回类型都具有修饰符。

基本上,因为 RHS 是非常不同的东西,所以适用不同的规则。

于 2021-08-24T08:48:52.010 回答
2

文档在代表部分中以不同的方式涵盖了这一点:

.NET Framework 3.5 在 C# 中的所有委托中引入了对方法签名与委托类型匹配的差异支持。这意味着您不仅可以将具有匹配签名的方法分配给委托,还可以分配返回更多派生类型(协变)或接受派生类型(逆变)少于委托类型指定的参数的方法。这包括通用和非通用委托。

因此,在您的情况下,这直接属于“接受派生类型(逆变)少于委托类型指定的参数的参数”部分。

如果你看到Sharplab的反编译,你会看到它FuncDelegate<DerivedClass> genericDerivedFunc2 = BaseFunc;实际上被转换成类似的东西:

FuncDelegate<DerivedClass> genericDerivedFunc2 = new FuncDelegate<DerivedClass>(BaseFunc);

并且genericDerivedFunc = genericBaseFunc;只是一个简单的分配,FuncDelegate<BaseClass>当它不是逆变FuncDelegate<DerivedClass>时将失败。FuncDelegate

于 2021-08-24T08:44:50.707 回答