为从List <string>继承的集合实现GetEnumerator()

Voj*_*ech 15 c# generics collections inheritance

我正在努力实施FilePathCollection.它的项目是简单的文件名(没有路径 - 例如"image.jpg").一旦通过foreach循环使用集合,它应该返回通过连接创建的完整路径baseDirectory.我怎样才能做到这一点?

public class FilePathCollection : List<string>
{
    string baseDirectory;

    public FilePathCollection(string baseDirectory)
    {
        this.baseDirectory = baseDirectory;
    }

    new public System.Collections.IEnumerator GetEnumerator()
    {
        foreach (string value in this._items) //this does not work because _list is private
            yield return baseDirectory + value;
    }
}
Run Code Online (Sandbox Code Playgroud)

Mat*_*hen 26

new public IEnumerator GetEnumerator()
{
  using(IEnumerator ie = base.GetEnumerator())
    while (ie.MoveNext()) {
      yield return Path.Combine(baseDirectory, ie.Current);
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 这看起来非常像'Enumerable.Select`的专门重写. (5认同)

Dan*_*ker 12

如果您有C#3,则无需编写特殊类来执行此操作.假设你有一个字符串,如序列List<string>或者string[],任何支持IEnumerable<string>,叫filePathCollection,你可以使用:

var prefixedPaths = filePathCollection.Select(path => baseDirectory + path);
Run Code Online (Sandbox Code Playgroud)

嘿presto - 你现在有一个IEnumerable<string>前缀的路径baseDirectory,所以你可以使用foreach它,等等.我们已经完成了.


本答案的其余部分是更一般的解释,以帮助您(和其他人)发现在其他情况下可以应用的地方.

Select本质上意味着:采取这一系列的项目,对每个项目做一些事情并给我一个包含所有结果的新序列.通过提供一个方法来指定"something",该方法接受与旧序列中存储的相同类型的一个参数,并返回您喜欢的任何类型,这将是新序列的项类型.在这种情况下,我们不会更改项目类型.我们还使用lambda定义了"就地"方法:

path => baseDirectory + path
Run Code Online (Sandbox Code Playgroud)

编译器计算出的源集合的元素类型string,所以pathstring-你能想到的path是打一个"循环变量"相同的作用,如果你有写了整个事情了自己.在path我们使用连接时,结果是另一个string,所以新的序列也必须是IEnumerable<string>.这是"类型推断",是这些东西减少你必须编写的代码量的重要部分.

发生的另一个重要事情是"封闭",这是我们如何使我们的lambda不仅依赖于它的"开放"参数path而且依赖于"封闭"参数的技术名称,该参数baseDirectory甚至没有明确地作为一个参数传递给它参数.lambda可以直接到达外部并获得可见的变量.这使得您无需编写构造函数,该构造函数将其baseDirectory作为参数存储并存储在_baseDirectory字段中,以便稍后可以在其他方法中重复使用它.

请注意,新序列的长度始终与传入序列的长度相同.如果要过滤掉项目,请使用Where.如果要延长序列,请使用SelectMany.