何时应该使用IEnumerable和GetEnumerator?

5
在我们的许多项目中,我看到了一些自定义集合/容器类,它们保存某种通用集合,例如一个 List(of T) 类。
它们通常有一个 GetXXX 方法,返回一个 IEnumerable,其中包含自定义集合类使用的任何类型,以便可以使用 foreach 循环遍历内部集合。
例如:
public IEnumerable<UploadState> GetStates
{
    get
    {
        return new List<UploadState>(m_states);
    }
}

我的问题是,这些类是否应该实现 IEnumerable 接口,并在 List 本身上调用 GetEnumerator

有没有更好的方法,还是由开发人员决定?


1
m_states的类型是什么?只是想知道为什么它被包装在new List<>中并作为IEnumerable<>返回。 - sll
http://msdn.microsoft.com/en-us/library/system.collections.ienumerable.getenumerator.aspx - Evan Mulawski
6个回答

2
我会这样做:
public IEnumerable<UploadState> GetStates
{
    get
    {
        foreach (var state in m_states) { 
            yield return state; 
        }
    }
}

这样做更加简洁,用户不会得到他们不应该拥有的列表(毕竟他们可以将其强制转换为List<T>),而且您不需要创建一个List<T>对象。 编辑:误解了问题。我认为如果这个类意味着是一个集合,它应该实现IEnumerable<T>

我认为这个问题并没有被问到,所以这可以作为评论而不是答案,但从重构的角度来看也是有意义的。 - sll

2
如果您的类是自定义集合类,那么它应该实现 IEnumerable<T> 接口。在这种情况下,内部列表的公共属性就是多余的。想象一个简单的类:
public class People : IEnumerable<Person>
{
    List<Person> persons = new List<Person>();

    public IEnumerator<Person> GetEnumerator()
    {
        return persons.GetEnumerator();
    }
}

但是如果你的类不能像集合一样运作,那么请为其元素提供一个公共的 IEnumerable 属性:

public class Flight
{
    List<Person> passengers = new List<Person>();

    public IEnumerable<Person> Passengers
    {
        get { return passengers; }
    }
}

无论如何,选择合适的设计总是由开发人员决定的。

谢谢Balazs Tihanyi,我可以问一个关于你的例子的傻问题吗?为什么你实现了IEnumerable<Person>而不是IEnumerable?我不确定。 - bobbo
你为什么想要这样做呢?IEnumerable<Person> 是泛型且类型安全的。IEnumerable 适用于 .NET 1.1 或匿名类型。如果你只实现 IEnumerable 接口,那么你需要将列表项强制转换为它们的类型。 - Balazs Tihanyi

1
考虑到您的代码示例中创建了一个列表。我不知道m_states是什么,但如果这是一个值类型集合,您可以创建原始列表的克隆。通过这种方式,返回的列表可以由调用者添加/删除/更新元素,而不会影响原始数据。
如果m_states是引用类型,则仍然创建一个列表,调用者可以再次操作该列表以添加/删除/更新元素(它是一个引用!),而不会影响原始数据。
至于IEnumerable<T>,它只是一种使返回类型成为通用类型的方法,并且不会对List<T>类型进行强耦合的方式。

m_states 是一种引用类型。它被声明为:private List<UploadState> m_states = new List<UploadState>();我认为方法返回值不能被操作是因为它返回的是一个 IEnumerable<UploadState>,而不是 List<UploadState>? - bobbo
@bobbo:当然可以,如果你从返回的集合中取出一个元素并更改其属性,则更改的是原始元素的属性,因为它是一种引用类型。 - Tigran
好的,这很有道理。但是,你的一句话"If m_states are reference types, this still creates a new list which can be again manipulated by the caller Add/Remove/ No update elements (it's a reference!) without affecting original data."让人感到困惑。你的意思是返回的新列表可以被添加、删除元素,但不能更新吗?我认为因为它返回了IEnuerable,所以你可以修改列表中的元素,比如改变属性,但不能添加和删除列表中的项。这是否正确? - bobbo
我的意思是:如果你想在那个列表上执行一些统计操作,删除其中的元素,添加新的聚合值(比如说),它都不会以任何方式影响m_states列表。 - Tigran

0

你应该基于 System.Collections.ObjectModel 命名空间中的一个类来派生你的自定义集合类。它们已经包含了 IEnumerable<T> 和非泛型的 IEnumerable 接口的实现。


0

我认为,如果你新实现的类的行为方式与列表相同,那么就没有必要实现它。如果你需要某种自定义逻辑,那就取决于你想做什么;你可以继承列表或者实现IEnumerable接口。这取决于要实现什么目标。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接