c# . net协方差——为了旧日的缘故再来一次

本文关键字:再来一次 net 方差 | 更新日期: 2023-09-27 18:05:13

所以我们有这个:

public interface IWidget
{
    int Id { get; set; }
}
public class Widget : IWidget
{
    public int Id { get; set; }
}
public class WidgetProcessor
{
    public static void ProcessWidgets1(IList<IWidget> widgets)
    { }
    public static void ProcessWidgets2<T>(IList<T> widgets) where T : IWidget
    { }
}

我明白为什么这不能编译:WidgetProcessor.ProcessWidgets1(new List<Widget>());c#有关协方差的规则明智地说不应该这样做,否则你可能会遇到各种各样的顽皮行为,正如在其他地方详细解释的那样。

但是ProcessWidgets2: what the…?
这个是如何编译和运行的呢?WidgetProcessor.ProcessWidgets2(new List<Widget>());

期待我的无知被移除,但我看不出ProcessWidgets1和ProcessWidgets2有什么不同。

c# . net协方差——为了旧日的缘故再来一次

ProcessWidgets2<T>是一个泛型方法。当您用new List<Widget>()调用它时,编译器推断类型TWidget,它匹配约束,并调用它,因为List<T>实现了IList<T>

这可能是最容易看到的,好像它被分解成多个调用:

IList<Widget> temp = new List<Widget>();
WidgetProcessor.ProcessWidgets2<Widget>(temp); // Widget is an IWidget, so this matches constraints

这里没有变化,因为List<T>直接实现了IList<T>,并且您使用编译器推断的特定类型调用

对于第一个示例,您可以执行以下代码:

widgets.Add(new SomeOtherWidget());

如果widgets实际上被允许为List<Widget>,那么你将把SomeOtherWidget放入Widget对象列表中。这很糟糕,因为当你取出一个对象时,它可能根本不是Widget

对于第二个示例,widgets.Add(new SomeOtherWidget());将无法编译。SomeOtherWidget不会是T类型。您只允许使用类型为T的对象调用列表中的Add,而不允许使用任何旧的IWidget对象,因此它能够维护类型安全。

相关文章: