【问题标题】:C#-Hierarchical Iteration works differentlyC#-分层迭代的工作方式不同
【发布时间】:2009-08-20 12:50:20
【问题描述】:

当我对以下声明进行迭代时:

汽车[]汽车=新车[2];
汽车[0] =新车{速度= 100,颜色=“绿色”};
汽车[1] = 新车 { Speed = 200, Color = "Orange" };

使用“foreach”我没有问题迭代 Car Array

但在以下情况下:

公共类 CarStore:Car {
Car[] m_collection = new Car[2];

    public CarStore()
    {
        m_collection[1] = new Car { Speed = 100, Color = "red" };
        m_collection[2] = new Car { Speed = 200, Color = "yellow" };
    }

}


现在我想使用:

汽车商店 m_Store = new CarStore();

foreach(m_Store 中的 Car myCar),直到我使用 IEnumerator,迭代才会起作用


我的问题是:
  1. CLR 在内部如何处理它们? .第一种情况它使用 System.Array,会发生什么 到第二个声明案例..?
  2. 我真的不适合在 OOps 世界中编码吗?因为不知道这些基本的东西?

【问题讨论】:

    标签: c#


    【解决方案1】:

    CLR 对foreach 真的一无所知。这都是关于 C# 编译器的:

    • 当 C# 编译器看到一个数组时,它将使用 Length 和数组索引器对其进行迭代。
    • 当 C# 编译器看到实现 IEnumerable<T>IEnumerable 的内容时,它将使用 GetEnumerator,然后使用 MoveNext/Current(最后是 Dispose)。
    • 当 C# 编译器看到带有 GetEnumerator 方法的东西被声明为返回具有适当 MoveNext()/Current 成员的东西时,它会使用它 - 但这实际上是为了提供类型安全的集合预泛型,并且不在新代码中使用。

    不知道这一点并不意味着你不适合编程——它只是意味着你应该在深入学习之前阅读一本关于 C# 的好书。无知和愚蠢的区别在于,无知是可以纠正的:)


    基本上你应该实现IEnumerable<Car>:

    public class CarStore : IEnumerable<Car> { 
        Car[] m_collection = new Car[2];
    
        public CarStore()
        {
            // Indexes adjusted - C# arrays are 0-based
            m_collection[0] = new Car { Speed = 100, Color = "red" };
            m_collection[1] = new Car { Speed = 200, Color = "yellow" };
        }
    
        public IEnumerator<Car> GetEnumerator() {
            return m_collection.GetEnumerator();
        }
    
        public IEnumerator IEnumerable.GetEnumerator() {
            return GetEnumerator();
        }
    }
    

    仅仅拥有方法而不实现IEnumerable&lt;T&gt; 就足以让编译器使用foreach,但是对于泛型,实现IEnumerable&lt;T&gt; 没有任何好处,这样你的类就可以用于LINQ等。

    顺便说一句,我已经删除了 CarStore : Car 的想法,因为在我看来,CarStore实际上Car... 在你的真实情况下它可能是当然更合理。

    【讨论】:

    • 有我自己的,“深度 C#”;还有“C# 3.0 in a Nutshell”和“Accelerated C# 2008”。
    • CLR 也通过 C# Rocks,顺便说一句。
    • 嗨,我订购了你的书!感谢您的信息
    • @Narresh 请参阅 msmvps.com/blogs/jon_skeet/archive/2008/11/19/… 了解 Jon 的一些演讲。
    • 或者一般来说,csharpindepth.com/Talks.aspx - 希望下周我将在都柏林提供另外两个链接:)
    【解决方案2】:

    foreach 无法神奇地推断出您正在构建一个可枚举的集合。它的作用是查看它是否可以调用返回IEnumeratorGetEnumerator 方法。如果不能调用这样的方法,编译器会报错。

    如果您希望能够在自己的对象上使用 foreach,则必须提供 GetEnumerator 实现:

    public IEnumerable<Car> GetEnumerator() {
        foreach(Car c in m_collection) // btw, don't use `s_`, `m_`, `g_` prefixes
            yield return c;
    }
    

    【讨论】:

    • 虽然我完全同意你不使用前缀的建议,但这似乎与手头的问题有点正交。
    • akmad:这就是为什么它只是一个评论,前缀为“btw”;)
    【解决方案3】:

    在您的示例中,您可以遍历汽车数组,因为它是一个数组,显然实现了 IEnumerable(请参阅下面的 Jons 评论)。您的类 CarStore 是 Car 类型的子类(我假设它是 object 的子类并且不实现 IEnumerable 接口)。您无法枚举 CarStore 类,因为您没有实现 IEnumerable 接口。

    如果您没有明确指定要在 CarStore 类型上枚举的内容,运行时应该如何解决?

    【讨论】:

    • 这对于 C# foreach 语句是不正确的。您不必实现 IEnumerable 或 IEnumerable,只需实现一个名为 GetEnumerator 且具有兼容签名的方法。
    • 事实上,对于数组,C# 编译器根本不使用 IEnumerable - 它只是使用数组索引器和 Length 进行迭代。
    • @Jon... 哎呀,谢谢你的信息! @Greg ...也不知道,我一直只是实现了接口。但是,我认为仅仅创建一个具有兼容签名的方法并不清楚,而且我肯定会在代码审查中指出这一点。很高兴知道它在那里!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-27
    • 1970-01-01
    • 2015-07-01
    • 2019-04-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多