【问题标题】:Foreach can throw an InvalidCastException?Foreach 可以抛出 InvalidCastException 吗?
【发布时间】:2009-06-04 10:44:01
【问题描述】:

想象一下下面的代码:

class foreach_convert
{
    public static void method2()
    {
        List<IComparable> x = new List<IComparable>();
        x.Add(5);

        foreach (string s in x)
        {
            //InvalidCastException in runtime
        }
    }
}

我想知道,为什么这种 foreach 行为如此......不像 C#? 这里发生的是对子类的隐式强制转换,这很容易出错,并且似乎在该语言的所有其他地方都被禁止。还是我说的不对?

附:我问的原因是我的项目中的类似代码中有一个错误,我曾经在其中迭代来自外部库的自定义集合,它被称为SomeTypeCollection,但实际上提供了一个基础集合键入项目并且可能包含 SomeOtherType 的项目。我的错,但语言和编译器仍然没有提供任何明确的提示/警告,这对于 C# 来说是非常不寻常的......

【问题讨论】:

  • 嘿,当我希望(不是真的期望)foreach 会根据对象的类型隐式过滤集合中的对象时,我第一次遇到了这个问题。想想它是怎么读的,“对于集合中的每个字符串”。对我来说,这读起来就像,“对于集合中的所有字符串,做一些事情。”然后它将忽略任何不是字符串的内容。
  • 历史记录:分析器被 dotnet/runtime 团队拒绝:github.com/dotnet/roslyn-analyzers/issues/4479
  • 更新:新的IDE0210分析器是created to detect this,将在VS2022 17.3中引入

标签: c# foreach language-design


【解决方案1】:

回想一下泛型之前...foreach 必须强制转换,以便您可以做一些明智的事情,例如:

foreach (string x in names)
{
    // ...
}

代替:

foreach (object tmp in names)
{
    string x = (string) tmp;
    // ...
}

后者太恶心了,IMO。提供隐式强制转换与语言的其他部分不同,但在绝大多数情况下更容易使用。

我怀疑如果C#有泛型和扩展方法开始(所以我们可以使用OfType和Cast)foreach不会' t 以完全相同的方式指定。

请注意,foreach 中还有更多奇怪之处:该类型根本不必实现 IEnumerable。只要它有一个GetEnumerator 方法,它返回的东西又具有MoveNext() 和Current,C# 编译器就很高兴。这意味着您可以在泛型之前实现“强类型迭代器”(以避免装箱)。

【讨论】:

  • 对。我怀疑那是第一个 C# 的遗产,现在使它成为强类型是一个重大变化...... [抱怨] 尽管如此,MSDN 中可能有一个警告或只是一个注释:-)
  • 好吧,现在精神振作了,并且能够理解...嗯...单词...不要以为我的回答添加了这里没有说的任何内容:)
  • 他们可以在没有泛型的情况下构建相关功能,而不是实现这种荒谬的隐式转换。
【解决方案2】:

对于 C# 3,我使用的是 var - 所以我收到编译器警告。

【讨论】:

  • 好点。在 foreach 中使用 var 可以使您免于该错误,因为它会检测您正在迭代的集合的确切类型。
【解决方案3】:

foreach 适用于 IEnumerable,它返回 object 类型的对象。对于每个项目,对象都将转换为您提供的类型。

除非你在 C# 3.0 中使用 var。然后类型将取自IEnumerable&lt;T&gt;,如果它是由集合实现的。

【讨论】:

  • 这给人的印象是 foreach (int x in List) 会先装箱然后再拆箱 - 但事实并非如此。 C# 编译器使用 IEnumerable 优先于可用的 IEnumerable。
  • 您对键入的界面是正确的。有时 C# 规范有点不一致:似乎 foreach 构造是 C# 中唯一允许协变和逆变的东西。
猜你喜欢
  • 2020-04-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-02
  • 1970-01-01
  • 2012-02-09
  • 2014-02-09
相关资源
最近更新 更多