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

时间:2010-03-21 06:45:36

标签: c# generics inheritance collections

我正在尝试实施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;
    }
}

5 个答案:

答案 0 :(得分:26)

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

答案 1 :(得分:12)

如果您有C#3,则无需编写特殊类来执行此操作。假设您有一系列字符串,例如List<string>string[],支持IEnumerable<string>的任何字符串,名为filePathCollection,您只需使用:

var prefixedPaths = filePathCollection.Select(path => baseDirectory + path);

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


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

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

”定义了“就地”方法
path => baseDirectory + path

编译器发现源集合的元素类型为string,因此pathstring - 您可以将path视为扮演相同的角色作为一个“循环变量”,如果你必须自己写出整个事情。在path上我们使用连接,因此结果是另一个string,因此新序列也必须是IEnumerable<string>。这是“类型推断”,是这些东西减少你必须编写的代码量的重要部分。

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

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

答案 2 :(得分:4)

使用new关键字会导致多态问题: 如果有人这样做:

List<string> files = new FilePathCollection();

调用foreach (var files in files)将导致调用未覆盖的枚举器。

我认为最好的是继承自IEnumerable<string>,并在您的列表中保留一个私有字段。

例如,这可能是一种方法:继承自List<string>已继承的IEnumerable<T>

 public class FilePathCollection :  IList<string>
    {
        string baseDirectory;
        private List<string> internalList;

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

        #region IList<string> Members

        public int IndexOf(string item)
        {
            return GetFileNameOnly(internalList.IndexOf(item));
        }
        private string GetFileNameOnly(string p)
        {
            //your implementation.......
            throw new NotImplementedException();
        }

        private int GetFileNameOnly(int p)
        {
           //your implementation.......
            throw new NotImplementedException();
        }

        public void Insert(int index, string item)
        {
            internalList.Insert(index, item);
        }

        public void RemoveAt(int index)
        {
            internalList.RemoveAt(index);
        }

        public string this[int index]
        {
            get
            {
                return GetFileNameOnly(internalList[index]);
            }
            set
            {
                this[index] = value;
            }
        }



        #endregion

        #region ICollection<string> Members

        public void Add(string item)
        {
            internalList.Add(item);
        }

        public void Clear()
        {
            internalList.Clear();
        }

        public bool Contains(string item)
        {
            return internalList.Contains(item);
        }

        public void CopyTo(string[] array, int arrayIndex)
        {
            internalList.CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get { return internalList.Count; }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

        public bool Remove(string item)
        {
            return internalList.Remove(item);
        }

        #endregion

        #region IEnumerable<string> Members

        public IEnumerator<string> GetEnumerator()
        {
            foreach(string value in internalList)
                yield return baseDirectory + value;
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            foreach(string value in internalList) 
                yield return baseDirectory + value;
        }

        #endregion
    }

答案 3 :(得分:1)

你可以使用base.GetEnumerator()并手动迭代它。

但是,我认为你会以你试图去做的方式遇到各种各样的问题。您应该从放入它们的列表中获取相同的值。例如,使用您显示的代码,您将获得枚举列表的不同值,而不是使用索引器时。是否清楚List的其他方法如Add()或Contains()?

有没有理由你不能创建一个接收文件名列表和基目录的静态方法,然后生成一个新的结果列表,其中每个元素都是dir + file?

答案 4 :(得分:0)

你可以转换为基类型。

new public System.Collections.IEnumerator GetEnumerator()
{
    foreach (string value in (List<string>)this) //<-- note the cast
        yield return baseDirectory + value;
}

理想情况下,我会使用Select扩展方法。请给它一个通用的过载...

public new IEnumerator<string> GetEnumerator()
{
    return ((List<string>)this).Select(x => baseDirectory + x).GetEnumerator();
}