【问题标题】:Contra/Covariance issue when assigning to Lazy<>分配给 Lazy<> 时的反/协方差问题
【发布时间】:2026-01-12 03:45:01
【问题描述】:
private void AMethod<T>() where T : Control, new()
{
    Lazy<T> lazyControl = new Lazy<T>(() => new T());

    Lazy<Control> a = lazyControl;
}

我在最后一行收到以下错误。

Argument 2: cannot convert from 'System.Lazy<T>' to
'System.Lazy<System.Windows.Forms.Control>'

我知道 T 可能是一个更具体的类型,但我不明白为什么我不能将它分配给 Lazy 变量。

【问题讨论】:

    标签: c# covariance contravariance


    【解决方案1】:

    我知道T 可能是一个更具体的类型,但我不明白为什么我不能将它分配给 Lazy 变量。

    Lazy&lt;T&gt;Lazy&lt;Control&gt; 没有关系,就像 Lazy&lt;Base&gt;Lazy&lt;Derived&gt; 有任何关系一样。

    仅仅因为Derived 派生自Base 而你可以这样做:

    Base b = new Derived();
    

    ...这并不意味着你可以这样做:

    Lazy<Base> b = new Lazy<Derived>();
    

    Lazy&lt;Derived&gt; 类型不是从Lazy&lt;Base&gt; 派生的。 @Jon Skeet 在这里很好地解释了这一点:

    C# variance problem: Assigning List<Derived> as List<Base>

    【讨论】:

      【解决方案2】:

      如果有一个ILazy&lt;T&gt; 接口,它可以声明为ILazy&lt;out T&gt;,在你的例子中一切都会好起来的:T is 仅用于输出位置,有效.

      但是,Lazy&lt;T&gt; 是一个类。协变/逆变只能为委托和接口指定,所以Lazy&lt;T&gt;不能指定它的协变。

      因此Lazy&lt;Control&gt;Lazy&lt;T&gt; 不兼容,这就是分配不起作用的原因。这绝对令人沮丧,至少考虑到当前的 API,从调用者的角度来看,协变是“安全的”。

      (如果您到处都需要这个,您可以声明自己的ILazy&lt;out T&gt; 接口,然后编写该接口的实现来包装Lazy&lt;T&gt;。我怀疑这比它的价值更麻烦。)

      【讨论】:

      • 嗯,有道理。我确实知道协/逆变只适用于接口。非常感谢。