【问题标题】:How to understand contravariance when using generic delegates?使用泛型委托时如何理解逆变?
【发布时间】:2021-10-24 11:39:02
【问题描述】:

我正在学习“逆变泛型委托”。

我的理解是:

“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 t)"。

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

但是不用“in”关键字也可以进行转换。

【问题讨论】:

  • 没有真正回答我的问题。

标签: c# generics inheritance delegates contravariant


【解决方案1】:

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

genericDerivedFunc = genericBaseFunc;
genericDerivedFunc2 = BaseFunc;

第一行的右手边是一个委托,所以你将一个委托类型转换为另一个委托类型。这需要 variance conversion,如 C# 规范中的可用转换中所列:

隐式引用转换为:

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

而方差转换需要ins 和outs。

虽然在第二行,右侧是一个方法组(方法的名称),所以在第二行,您实际上是在执行method group conversion。要使此类转换可用,BaseFunc 需要为具有目标委托类型的 compatible。请注意,这是对方法的要求,而不是对委托类型的要求。要“兼容”。

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

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

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

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

【讨论】:

    【解决方案2】:

    文档在variance in delegates 部分中对此进行了介绍:

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

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

    如果你看到sharplab 的反编译,你会看到FuncDelegate&lt;DerivedClass&gt; genericDerivedFunc2 = BaseFunc; 实际上被转换成类似的东西:

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

    genericDerivedFunc = genericBaseFunc; 只是将FuncDelegate&lt;BaseClass&gt; 分配给FuncDelegate&lt;DerivedClass&gt; 的简单赋值,当FuncDelegate 不是逆变时,它将失败。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-09-17
      • 2013-11-26
      • 1970-01-01
      • 2011-04-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多