【问题标题】:Why is a cast to an `IEnumerable<T>` allowed but to a `List<T>` in a generic method [duplicate]为什么在泛型方法中允许转换为`IEnumerable<T>`但转换为`List<T>`[重复]
【发布时间】:2019-11-18 00:40:32
【问题描述】:

我很确定我错过了明显的答案,但为什么会这样:

public static void Foo(IEnumerable<string> strings) { }

public static void Bar<T>(IEnumerable<T> ts)
{
    Foo((IEnumerable<string>)ts);
}

是允许的,而这个:

public static void Foo(List<string> strings) { }

public static void Bar<T>(List<T> ts)
{
    Foo((List<string>)ts);
}

CS0030 Cannot convert type 'System.Collections.Generic&lt;T&gt;' to type 'System.Collections.Generic&lt;string&gt;'. 失败?两者都有可能在运行时失败,并且都有可能在运行时成功。这是什么语言规则?

【问题讨论】:

  • 这能回答你的问题吗? Why can't I cast from a List<MyClass> to List<object>? 尤其是声明说:“这是安全的,因为 IEnumerable&lt;T&gt; 没有公开任何接受 T 的方法。
  • @BagusTesa 特别是 T 被定义为 out 仅用于 IEnumerable ,它强制您声明 IEnumerable 不接受任何类型的 T
  • 不,它没有,“重复”问题似乎也没有。我知道什么是通用方差,但是允许从IEnumerable&lt;T&gt;IEnumerable&lt;string&gt; 任意T 的转换是不安全的。 IEnumerable&lt;T&gt; 必须是协变和逆变的,才能在运行时永远不会失败。

标签: c# generics types casting


【解决方案1】:

我认为这是以下问题的边缘案例示例:
Cast List<T> to List<Interface>

我同意在这种情况下它看起来很奇怪,因为不可能从扩展字符串的东西(字符串类是密封的)进行转换,但我猜规则就是你不能转换 @ 987654322@ 到 List&lt;SomethingElse&gt; 因为在 T 是扩展了 SomethingElse 类或实现名为 SomethingElse 的接口的类的情况下,可能会调用该方法。

例如:

public static void Foo(List<string> strings) { }

public static void Bar<T>(List<T> ts)
{
    Foo((List<MyInterface>)ts);
}

public static void OtherMethod1()
{
    Bar<MyClass>(new List<MyClass>)
}

public static void OtherMethod2()
{
    Bar<MyInterface>(new List<MyInterface>)
}

public classs MyClass : MyInterface
{
}

因此,因为可以编写像 OtherMethod1 这样在运行时失败的方法,所以编译器不允许整个场景。
不过,如果 Microsoft 愿意,密封类似乎是一种奇怪的边缘情况。

【讨论】:

  • 但是使用T = object 调用IEnumerable&lt;T&gt; 版本也会在运行时失败,但这是允许的。为什么?
  • 允许转换在运行时失败。编译器不允许的是可以工作的强制转换,但在尝试调用强制转换类型的有效方法时会失败。如果您从 T 转换为 object 并且转换成功,则 IEnumerable 上没有任何方法会中断。有列表。请参阅链接的示例/副本。
猜你喜欢
  • 1970-01-01
  • 2012-02-24
  • 2021-12-02
  • 2018-08-04
  • 2011-09-30
  • 2014-12-07
  • 1970-01-01
  • 2019-06-15
相关资源
最近更新 更多