Câu hỏi Thực hiện GetEnumerator () cho một bộ sưu tập được kế thừa từ List


Tôi đang cố gắng triển khai FilePathCollection. Các mục của nó sẽ là các tên tệp đơn giản (không có đường dẫn - chẳng hạn như "image.jpg"). Sau khi bộ sưu tập được sử dụng qua foreach chu kỳ, nó sẽ trả lại đường dẫn đầy đủ được tạo bằng cách nối với baseDirectory. Làm thế nào tôi có thể làm điều đó?

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;
    }
}

15
2018-03-21 06:45


gốc


Dù bạn làm gì, hãy sử dụng Path.Combine( baseDirectory, value )  msdn.microsoft.com/en-us/library/fyy7a5kt.aspx . - Danko Durbić


Các câu trả lời:


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

26
2018-03-21 07:07



rực rỡ, đó là nó :-) cảm ơn - Vojtech
Điều này trông rất giống như viết lại chuyên ngành Enumerable.Select. - Daniel Earwicker
Daniel, tôi đã chỉ ra cách tiếp cận phương pháp mà anh đã chọn. Bạn có một lý lẽ tốt cho việc không sử dụng lớp học. - Matthew Flaschen


Nếu bạn có C # 3, bạn không cần phải viết một lớp đặc biệt để làm điều này. Giả sử bạn có một chuỗi các chuỗi, chẳng hạn như List<string> hoặc là string[], bất cứ thứ gì hỗ trợ IEnumerable<string>, gọi là filePathCollection, bạn chỉ có thể sử dụng:

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

Hey presto - bây giờ bạn có một IEnumerable<string> của các đường dẫn được bắt đầu bằng baseDirectory, vì vậy bạn có thể sử dụng foreach trên đó, v.v.


Phần còn lại của câu trả lời này là giải thích tổng quát hơn để giúp bạn (và những người khác) phát hiện ra nơi điều này có thể được áp dụng trong các trường hợp khác.

Select về cơ bản có nghĩa là: lấy chuỗi các mục này, làm điều gì đó với mỗi mục và trả lại cho tôi một chuỗi mới chứa tất cả các kết quả. "Cái gì đó" được xác định bằng cách cung cấp một phương thức chấp nhận một tham số cùng loại như được lưu trữ trong trình tự cũ và trả về bất kỳ loại nào bạn thích, đây sẽ là loại mục của chuỗi mới. Trong trường hợp này, chúng tôi không thay đổi loại mục. Ngoài ra chúng tôi đang xác định phương pháp "tại chỗ" bằng cách sử dụng lambda:

path => baseDirectory + path

Trình biên dịch chỉ ra rằng loại phần tử của bộ sưu tập nguồn là string, vì thế path là một string - bạn có thể nghĩ path khi đóng vai trò giống như "biến vòng lặp" nếu bạn phải viết toàn bộ nội dung của chính mình. Và hơn thế nữa path chúng tôi sử dụng nối, vì vậy kết quả là một string, do đó, trình tự mới cũng phải là IEnumerable<string>. Đây là "suy luận kiểu" và là một phần quan trọng trong cách công cụ này làm giảm số lượng mã bạn phải viết.

Điều quan trọng khác xảy ra là "đóng cửa", đó là tên kỹ thuật cho cách chúng ta làm cho lambda của chúng ta phụ thuộc không chỉ vào tham số "mở" của nó path mà còn trên tham số "đã đóng" baseDirectory mà thậm chí không được truyền vào nó một cách rõ ràng như một tham số. Một lambda chỉ có thể tiếp cận bên ngoài chính nó và nhận được các biến có thể nhìn thấy được phương thức nó được định nghĩa bên trong. Điều này đặc biệt giúp bạn giải phóng sự cần thiết phải viết một hàm tạo baseDirectory như một tham số và lưu trữ nó trong một _baseDirectory để bạn có thể sử dụng nó sau này nhiều lần trong một số phương pháp khác.

Lưu ý rằng chuỗi mới sẽ luôn có cùng độ dài với chuỗi sắp tới. Nếu bạn muốn lọc các mục, hãy sử dụng Where. Nếu bạn muốn tạo chuỗi dài hơn, hãy sử dụng SelectMany.


12
2018-03-21 08:29



+1. Giải pháp đơn giản cho một vấn đề đơn giản. HÔN :) - Roman Boiko
Roman, bạn đang làm tôi đỏ mặt! - Daniel Earwicker
+1 thích giải pháp ngắn này. - Pablo Retyk


sử dụng từ khóa mới có thể khiến bạn gặp vấn đề về đa hình: trong trường hợp một số có:

List<string> files = new FilePathCollection();

gọi điện thoại foreach (var files in files) sẽ gây ra để gọi cho điều tra viên không bị ghi đè.

Tôi nghĩ điều tốt nhất được kế thừa từ IEnumerable<string> và giữ một trường riêng với Danh sách của bạn.

Ví dụ, đây có thể là một cách để làm điều đó: kế thừa từ tôiList<string> mà nó đã được thừa hưởng từ 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
    }

4
2018-03-21 07:16



Thật vậy, thành phần thay vì thừa kế. - Dykam
Đây là một cách khá dài để viết lại Enumerable.Select. - Daniel Earwicker


Có lẽ bạn có thể sử dụng base.GetEnumerator () và tự động lặp lại điều đó.

Tuy nhiên, tôi nghĩ rằng bạn sẽ chạy vào tất cả các loại vấn đề thiết kế lớp theo cách bạn đang cố gắng làm điều đó. Bạn sẽ nhận được cùng một giá trị trong danh sách mà bạn đưa vào danh sách. Ví dụ: với mã bạn hiển thị, bạn sẽ nhận được các giá trị khác nhau liệt kê danh sách so với khi sử dụng trình chỉ mục. Ngoài ra, nó sẽ được rõ ràng những phương pháp khác của Danh sách làm như Add () hoặc Chứa ()?

Có một lý do bạn không thể chỉ cần tạo ra một phương pháp tĩnh mà có trong một danh sách tên tập tin và thư mục cơ sở, sau đó tạo ra một danh sách kết quả mới, nơi mỗi phần tử là dir + file?


1
2018-03-21 07:08





Bạn chỉ có thể truyền đến loại cơ sở.

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

Lý tưởng nhất là tôi sẽ chỉ sử dụng Select phương pháp mở rộng. Và cung cấp cho nó một quá tải chung xin vui lòng ...

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

0
2018-06-10 10:45