Tại sao sử dụng IList hoặc List?


82

Tôi biết đã có rất nhiều bài viết về vấn đề này nhưng nó vẫn khiến tôi bối rối tại sao bạn nên chuyển vào một giao diện như IList và trả lại một giao diện như IList thay vì danh sách cụ thể.

Tôi đã đọc rất nhiều bài đăng nói rằng cách này giúp thay đổi cách triển khai dễ dàng hơn sau này, nhưng tôi không thấy đầy đủ về cách hoạt động của nó.

Nói nếu tôi có phương pháp này

  public class SomeClass
    {
        public bool IsChecked { get; set; }
    }

 public void LogAllChecked(IList<SomeClass> someClasses)
    {
        foreach (var s in someClasses)
        {
            if (s.IsChecked)
            {
                // log 
            }
        }
    }

Tôi không chắc việc sử dụng IList sẽ giúp ích gì cho tôi trong tương lai.

Làm thế nào về nếu tôi đã ở trong phương pháp? Tôi vẫn nên sử dụng IList?

public void LogAllChecked(IList<SomeClass> someClasses)
    {
        //why not List<string> myStrings = new List<string>()
        IList<string> myStrings = new List<string>();

        foreach (var s in someClasses)
        {
            if (s.IsChecked)
            {
                myStrings.Add(s.IsChecked.ToString());
            }
        }
    }

Tôi nhận được gì khi sử dụng IList bây giờ?

public IList<int> onlySomeInts(IList<int> myInts)
    {
        IList<int> store = new List<int>();
        foreach (var i in myInts)
        {
            if (i % 2 == 0)
            {
                store.Add(i);
            }
        }

        return store;
    }

Còn bây giờ thì sao? Có một số triển khai mới của danh sách các int mà tôi sẽ cần thay đổi không?

Về cơ bản, tôi cần xem một số ví dụ mã thực tế về cách sử dụng IList sẽ giải quyết một số vấn đề khi chỉ đưa Danh sách vào mọi thứ.

Từ bài đọc của tôi, tôi nghĩ rằng tôi có thể đã sử dụng IEnumberable thay vì IList vì tôi chỉ đang lặp lại các thứ.

Chỉnh sửa Vì vậy, tôi đã xem xét một số phương pháp của tôi về cách thực hiện việc này. Tôi vẫn không chắc chắn về kiểu trả về (nếu tôi nên làm cho nó cụ thể hơn hoặc một giao diện).

 public class CardFrmVm
    {
        public IList<TravelFeaturesVm> TravelFeaturesVm { get; set; }
        public IList<WarrantyFeaturesVm> WarrantyFeaturesVm { get; set; }

        public CardFrmVm()
        {
            WarrantyFeaturesVm = new List<WarrantyFeaturesVm>();
            TravelFeaturesVm = new List<TravelFeaturesVm>();
        }
}

 public class WarrantyFeaturesVm : AvailableFeatureVm
    {
    }

 public class TravelFeaturesVm : AvailableFeatureVm
    {
    }

 public class AvailableFeatureVm
    {
        public Guid FeatureId { get; set; }
        public bool HasFeature { get; set; }
        public string Name { get; set; }
    }


        private IList<AvailableFeature> FillAvailableFeatures(IEnumerable<AvailableFeatureVm> avaliableFeaturesVm)
        {
            List<AvailableFeature> availableFeatures = new List<AvailableFeature>();
            foreach (var f in avaliableFeaturesVm)
            {
                if (f.HasFeature)
                {
                                                    // nhibernate call to Load<>()
                    AvailableFeature availableFeature = featureService.LoadAvaliableFeatureById(f.FeatureId);
                    availableFeatures.Add(availableFeature);
                }
            }

            return availableFeatures;
        }

Bây giờ tôi trả lại IList vì thực tế đơn giản là sau đó tôi sẽ thêm cái này vào mô hình miền của mình, cái có thuộc tính như sau:

public virtual IList<AvailableFeature> AvailableFeatures { get; set; }

Trên đây là bản thân IList vì đây là tiêu chuẩn để sử dụng với nhibernate. Nếu không, tôi có thể đã trả lại IEnumberable trở lại nhưng không chắc. Tuy nhiên, tôi không thể tìm ra người dùng 100% sẽ cần gì (đó là nơi mà việc trả lại bê tông có lợi thế hơn).

Chỉnh sửa 2

Tôi cũng đang nghĩ điều gì sẽ xảy ra nếu tôi muốn chuyển bằng tham chiếu trong phương pháp của mình?

private void FillAvailableFeatures(IEnumerable<AvailableFeatureVm> avaliableFeaturesVm, IList<AvailableFeature> toFill)
            {

                foreach (var f in avaliableFeaturesVm)
                {
                    if (f.HasFeature)
                    {
                                                        // nhibernate call to Load<>()
                        AvailableFeature availableFeature = featureService.LoadAvaliableFeatureById(f.FeatureId);
                        toFill.Add(availableFeature);
                    }
                }
            }

tôi sẽ gặp vấn đề với điều này? Vì chúng không thể truyền vào một mảng (có kích thước cố định)? Sẽ tốt hơn nếu có một Danh sách cụ thể?

Câu trả lời:


156

Có ba câu hỏi ở đây: tôi nên sử dụng kiểu nào cho một tham số chính thức? Tôi nên sử dụng gì cho một biến cục bộ? và những gì tôi nên sử dụng cho một loại trả lại?

Các thông số chính thức:

Nguyên tắc ở đây là không yêu cầu nhiều hơn mức bạn cần . IEnumerable<T>thông báo "Tôi cần lấy các phần tử của chuỗi này từ đầu đến cuối". IList<T>giao tiếp "Tôi cần lấy và đặt các phần tử của chuỗi này theo thứ tự tùy ý". List<T>thông báo "Tôi cần lấy và đặt các phần tử của chuỗi này theo thứ tự tùy ý và tôi chỉ chấp nhận danh sách; tôi không chấp nhận mảng."

Bằng cách yêu cầu nhiều hơn những gì bạn cần, bạn (1) khiến người gọi làm những việc không cần thiết để thỏa mãn những yêu cầu không cần thiết của bạn, và (2) truyền đạt sự giả dối cho người đọc. Chỉ hỏi những gì bạn sẽ sử dụng. Bằng cách đó, nếu người gọi có một chuỗi, họ không cần gọi ToList trên đó để đáp ứng nhu cầu của bạn.

Biến cục bộ:

Sử dụng bất cứ điều gì bạn muốn. Đó là phương pháp của bạn. Bạn là người duy nhất có thể xem chi tiết triển khai nội bộ của phương pháp.

Loại trả lại:

Nguyên tắc tương tự như trước, đảo ngược. Cung cấp mức tối thiểu mà người gọi của bạn yêu cầu. Nếu người gọi chỉ yêu cầu khả năng liệt kê chuỗi, chỉ cho họ một IEnumerable<T>.


9
Làm thế nào để bạn biết những gì người gọi cần mặc dù. Ví dụ, tôi đang chuyển một trong các kiểu trả về của mình thành IList <> thì tôi cũng có thể sẽ liệt kê chúng dù sao thì hãy chỉ trả về một IEnumberable. Sau đó, tôi xem trong chế độ xem của mình (mvc) và thấy rằng tôi thực sự cần phương thức đếm vì tôi cần sử dụng vòng lặp for. Vì vậy, trong ứng dụng của riêng tôi, tôi đã ước tính những gì tôi thực sự cần làm thế nào để bạn dự đoán những gì người khác sẽ cần hoặc không cần.
— chobo2

@ chobo2: Trong ví dụ cụ thể của bạn, LINQ Counthoạt động ở O (1) nếu của bạn IEnumerablelà ICollection. Tất nhiên, bạn cũng có thể chỉ sử dụng một foreachvòng lặp.
— Brian

6
@ chobo2: Chà, bạn dự đoán người gọi sẽ cần những phương thức nào? Đó có vẻ như là vấn đề cần giải quyết đầu tiên. Có lẽ bằng cách nào đó bạn có cách biết phương pháp nào để viết cho những người sắp gọi họ. Hỏi những người đó xem họ muốn các phương pháp trả về. Câu hỏi của bạn về cơ bản là "làm thế nào để tôi biết những gì phần mềm để viết?" Bạn biết bằng cách tìm hiểu những vấn đề mà khách hàng của bạn phải giải quyết và viết mã giải quyết vấn đề của họ.
— Eric Lippert

1
@Eric - bạn nên cập nhật câu trả lời của mình cho các tham số chính thức để bao gồm một ví dụ về ICollection nơi bạn chỉ cần thêm một mục. Ngoài ra, giải thích loại trả lại của bạn nên nói điều gì đó dọc theo dòng 'chỉ cung cấp mức tối thiểu nhất cho những gì bạn cho phép người gọi thực hiện'. Nếu nó chỉ đọc danh sách trở lại chỉ IEnumerable vv
— Charles Lambert

4
@kvb: Gần như vậy. Hãy xem xét kịch bản đầu tiên của bạn. Bạn có thể thực hiện cải tiến thuật toán bằng cách thực hiện items as IList<T>và nếu bạn nhận được mã không phải null, thì hãy sử dụng mã cải tiến của bạn. Bằng cách đó bạn tận dụng lợi thế nếu bạn có thể, trong khi vẫn cho phép sự linh hoạt của khách hàng trong những gì họ vượt qua trong.
— Eric Lippert

33

Lý do thiết thực nhất mà tôi từng thấy được Jeffrey Richter đưa ra trong CLR thông qua C #.

Mẫu là lấy lớp hoặc giao diện cơ bản nhất có thể cho các đối số của bạn và trả về lớp hoặc giao diện cụ thể nhất có thể cho các kiểu trả về của bạn. Điều này mang lại cho người gọi của bạn sự linh hoạt nhất trong việc chuyển các loại cho phương thức của bạn và nhiều cơ hội nhất để truyền / sử dụng lại các giá trị trả về.

Ví dụ, phương pháp sau

public void PrintTypes(IEnumerable items) 
{ 
    foreach(var item in items) 
        Console.WriteLine(item.GetType().FullName); 
}

cho phép phương thức được gọi là truyền trong bất kỳ kiểu nào có thể được truyền đến một kiểu liệt kê . Nếu bạn cụ thể hơn

public void PrintTypes(List items)

sau đó, giả sử, nếu bạn có một mảng và muốn in tên kiểu của chúng vào bảng điều khiển, trước tiên bạn phải tạo một Danh sách mới và điền vào đó với các kiểu của bạn. Và, nếu bạn đã sử dụng một triển khai chung, bạn sẽ chỉ có thể sử dụng một phương thức chỉ hoạt động cho bất kỳ đối tượng nào với các đối tượng thuộc một loại cụ thể.

Khi nói về các loại trả lại, bạn càng cụ thể thì người gọi càng linh hoạt hơn với nó.

public List<string> GetNames()

bạn có thể sử dụng kiểu trả về này để lặp lại tên

foreach(var name in GetNames())

hoặc bạn có thể lập chỉ mục trực tiếp vào bộ sưu tập

Console.WriteLine(GetNames()[0])

Trong khi đó, nếu bạn nhận được một loại ít cụ thể hơn

public IEnumerable GetNames()

bạn sẽ phải xoa bóp kiểu trả về để nhận giá trị đầu tiên

Console.WriteLine(GetNames().OfType<string>().First());

10
Lưu ý rằng lời khuyên này mâu thuẫn với câu trả lời của Eric Lippert trong khuyến nghị về các loại trả lại. Cách tiếp cận của Jeffrey Richter mang lại cho người tiêu dùng phương pháp sự linh hoạt nhất để sử dụng đối tượng trả về theo cách họ thích, trong khi của Eric cung cấp cho người bảo trì phương pháp sự linh hoạt nhất để thay đổi việc triển khai mà không cần sửa đổi bề mặt công khai của phương pháp. Tôi có xu hướng làm theo lời khuyên của Jeffrey đối với mã nội bộ, nhưng đối với thư viện công cộng, có lẽ tôi sẽ có xu hướng làm theo lời của Eric hơn.
— phoog

3
@phoog: Xem xét Eric đến từ đâu, sẽ không ngạc nhiên khi anh ấy thận trọng hơn về việc phá vỡ các thay đổi. Nhưng nó chắc chắn là một điểm hợp lệ.

Rất khó khăn. Có vẻ như có người cho cả hai bên (trả lại trần min hoặc không). Tôi hiểu thêm lý do tại sao bạn làm điều đó cho các thông số mà nó giúp dừng việc chuyển đổi không cần thiết. Tôi vẫn không chắc phải làm gì với kiểu trả về.
— chobo2

@ chobo2: Thực sự, bạn chỉ cần xem xét liệu bạn có thể cần thay đổi kiểu trả về trong tương lai nếu bạn chỉ định một cái gì đó chính xác hơn thay vì chung chung. Nếu bạn có thể xem xét phương pháp của mình, hãy xác định rằng bạn có thể sẽ không thay đổi loại bộ sưu tập trả lại, thì có thể an toàn để trả về một loại chính xác hơn. Nếu bạn không chắc chắn hoặc sợ rằng nếu bạn thay đổi nó trong tương lai, bạn sẽ phá vỡ mã của người khác, thì hãy đi tổng quát hơn.

1
+1 Thông tin bên lề: câu trả lời này là một ví dụ hay, cụ thể về CLR của Định luật Postel . :)
— Dan J

13

IEnumerable<T>cho phép bạn lặp lại qua một bộ sưu tập. ICollection<T>xây dựng dựa trên điều này và cũng cho phép thêm và xóa các mục. IList<T>cũng cho phép truy cập và sửa đổi chúng tại một chỉ mục cụ thể. Bằng cách giới thiệu phương pháp mà bạn mong đợi người tiêu dùng của mình làm việc cùng, bạn có thể tự do thay đổi cách triển khai của mình. List<T>sẽ triển khai cả ba giao diện đó.

Nếu bạn để lộ thuộc tính của mình như một List<T>hoặc thậm chí là IList<T>khi tất cả những gì bạn muốn người tiêu dùng của mình có là khả năng lặp lại qua bộ sưu tập. Sau đó, họ có thể phụ thuộc vào thực tế rằng họ có thể sửa đổi danh sách. Sau đó, nếu bạn quyết định chuyển đổi kho dữ liệu thực tế từ a List<T>sang a Dictionary<T,U>và hiển thị các khóa từ điển làm giá trị thực cho thuộc tính (tôi đã phải làm chính xác điều này trước đây). Sau đó, những người tiêu dùng đã mong đợi rằng những thay đổi của họ sẽ được phản ánh bên trong lớp học của bạn sẽ không còn khả năng đó nữa. Đó là một vấn đề lớn! Nếu bạn để lộ List<T>như một, IEnumerable<T>bạn có thể thoải mái dự đoán rằng bộ sưu tập của bạn không bị sửa đổi bên ngoài. Đó là một trong những quyền hạn của việc hiển thị List<T>như bất kỳ giao diện nào ở trên.

Mức trừu tượng này đi theo hướng khác khi nó thuộc về các tham số của phương thức. Khi bạn chuyển danh sách của mình đến một phương thức chấp nhận, IEnumerable<T>bạn có thể chắc chắn rằng danh sách của mình sẽ không bị sửa đổi. Khi bạn là người triển khai phương pháp và bạn nói rằng bạn chấp nhận một IEnumerable<T>bởi vì tất cả những gì bạn cần làm là lặp lại danh sách đó. Sau đó, người gọi phương thức có thể tự do gọi nó với bất kỳ kiểu dữ liệu nào có thể liệt kê được. Điều này cho phép mã của bạn được sử dụng theo những cách không mong muốn, nhưng hoàn toàn hợp lệ.

Từ đó, việc triển khai phương thức của bạn có thể đại diện cho các biến cục bộ của nó theo cách bạn muốn. Các chi tiết thực hiện không được tiết lộ. Giúp bạn có thể tự do thay đổi mã của mình thành thứ gì đó tốt hơn mà không ảnh hưởng đến những người đang gọi mã của bạn.

Bạn không thể đoán trước được tương lai. Giả sử rằng loại thuộc tính sẽ luôn có lợi như một List<T>thuộc tính ngay lập tức hạn chế khả năng của bạn để thích ứng với những kỳ vọng không lường trước về mã của bạn. Có, bạn có thể không bao giờ thay đổi kiểu dữ liệu đó từ a List<T>nhưng bạn có thể chắc chắn rằng nếu bạn phải làm như vậy. Mã của bạn đã sẵn sàng cho nó.


9

Câu trả lời ngắn:

Bạn vượt qua giao diện để cho dù bạn sử dụng cách triển khai cụ thể nào của giao diện đó, mã của bạn sẽ hỗ trợ nó.

Nếu bạn sử dụng một triển khai danh sách cụ thể, thì mã của bạn sẽ không hỗ trợ một triển khai khác của cùng một danh sách.

Đọc một chút về kế thừa và đa hình .


8

Đây là một ví dụ: Tôi đã từng có một dự án trong đó danh sách của chúng tôi rất lớn, và kết quả là sự phân mảnh của đống đối tượng lớn đã làm ảnh hưởng đến hiệu suất. Chúng tôi đã thay thế Danh sách bằng LinkedList. LinkedList không chứa một mảng, vì vậy đột nhiên, chúng tôi gần như không sử dụng đống đối tượng lớn.

Hầu hết, chúng tôi đã sử dụng các danh sách IEnumerable<T>, vì vậy không cần thay đổi gì thêm. (Và vâng, tôi khuyên bạn nên khai báo các tham chiếu là IEnumerable nếu tất cả những gì bạn đang làm là liệt kê chúng.) Ở một số nơi, chúng tôi cần trình lập chỉ mục danh sách, vì vậy chúng tôi đã viết một IList<T>trình bao bọc không hiệu quả xung quanh các danh sách được liên kết. Chúng tôi không thường xuyên cần đến trình lập chỉ mục danh sách, vì vậy sự kém hiệu quả không phải là vấn đề. Nếu đúng như vậy, chúng tôi có thể cung cấp một số triển khai khác của IList, có lẽ là một tập hợp các mảng đủ nhỏ, sẽ có thể lập chỉ mục hiệu quả hơn trong khi cũng tránh được các đối tượng lớn.

Cuối cùng, bạn có thể cần phải thay thế một triển khai vì bất kỳ lý do gì; hiệu suất chỉ là một khả năng. Bất kể lý do là gì, việc sử dụng loại có nguồn gốc ít nhất có thể sẽ làm giảm nhu cầu thay đổi trong mã của bạn khi bạn thay đổi loại thời gian chạy cụ thể của các đối tượng.


3

Bên trong phương thức, bạn nên sử dụng var, thay vì IListhoặc List. Khi nguồn dữ liệu của bạn thay đổi để đến từ một phương pháp, onlySomeIntsphương pháp của bạn sẽ tồn tại.

Lý do để sử dụng IListthay vì Listdưới dạng tham số, là vì nhiều thứ triển khai IList(Danh sách và [], như hai ví dụ), nhưng chỉ một thứ được triển khai List. Nó linh hoạt hơn để viết mã cho giao diện.

Nếu bạn chỉ liệt kê các giá trị, bạn nên sử dụng IEnumerable. Mọi loại kiểu dữ liệu có thể chứa nhiều hơn một giá trị sẽ được triển khai IEnumerable(hoặc nên) và làm cho phương pháp của bạn cực kỳ linh hoạt.


13
Nói rằng anh ấy nên sử dụng "var" là hoàn toàn không chính xác. Không quan trọng anh ta sử dụng gì ở đó - đây là vấn đề về phong cách. Nó không ảnh hưởng đến chữ ký của phương thức và được thiết lập sẵn tại thời điểm biên dịch. Thay vào đó, bạn nên giúp anh ấy vượt qua sự bối rối về việc khai báo địa phương của anh ấy như IList foo = new List - đây là nơi rõ ràng sự nhầm lẫn của anh ấy nằm ở chỗ.
— x0n

Vì vậy, về cơ bản bạn nói rằng hãy tham gia IList chỉ vì thực tế là nếu họ muốn gửi một mảng mà họ không phải làm. Làm thế nào về việc trả lại nó? Tôi không hiểu ý của bạn là "method sẽ tồn tại" khi sử dụng vars? Tôi biết nó sẽ có nhiều công việc hơn một chút nhưng bạn sẽ không phải thay đổi nó sang loại mới? Vì vậy, có thể IList <int> thành IList <Chuỗi>?
— chobo2

Đúng vậy, quan điểm của "sử dụng var" là một gợi ý để không lo lắng về nó bên trong chính phương pháp và tập trung hơn vào cách nó trông như thế nào đối với người tiêu dùng.
— Bryan Boettcher

Tôi đồng ý với @ x0n: var đã được sử dụng quá mức và sẽ không giúp làm rõ hơn điều gì.
— Đó là Chuck Guy

1

Việc sử dụng IList thay vì List giúp việc viết các bài kiểm tra đơn vị dễ dàng hơn đáng kể. Nó cho phép bạn sử dụng thư viện 'Mocking' để chuyển và trả lại dữ liệu.

Lý do chung khác cho việc sử dụng giao diện là để hiển thị lượng kiến ​​thức tối thiểu cần thiết cho người dùng của một đối tượng.

Hãy xem xét trường hợp (tiếp theo) trong đó tôi có một đối tượng dữ liệu triển khai IList.

public class MyDataObject : IList<int>
{
    public void Method1()
    {
       ...
    }
    // etc
}

Các hàm của bạn ở trên chỉ quan tâm đến việc có thể lặp lại một danh sách. Tốt nhất là họ không cần biết ai thực hiện danh sách đó hoặc cách họ thực hiện danh sách đó.

Trong ví dụ của bạn, IEnumerable là một lựa chọn tốt hơn như bạn nghĩ.


1

Bạn nên giảm thiểu sự phụ thuộc giữa các mã của bạn càng nhiều càng tốt.

Ghi nhớ điều này, điều hợp lý nhất là chuyển các kiểu với số lượng phụ thuộc bên ngoài ít nhất có thể và trả về cùng một. Tuy nhiên, điều này có thể khác tùy thuộc vào khả năng hiển thị của các phương pháp của bạn và chữ ký của chúng.

Nếu các phương thức của bạn tạo thành một phần của giao diện, thì các phương thức sẽ cần được xác định bằng các loại có sẵn cho giao diện đó. Các loại bê tông có thể sẽ không có sẵn cho các giao diện, vì vậy chúng sẽ phải trả về các loại không phải là bê tông. Ví dụ: bạn sẽ muốn làm điều này nếu bạn đang tạo một khuôn khổ.

Tuy nhiên, nếu bạn không viết một khung công tác, có thể có lợi khi chuyển tham số với các kiểu yếu nhất có thể (tức là các lớp cơ sở, giao diện hoặc thậm chí là đại biểu) và trả về các kiểu cụ thể. Điều đó cung cấp cho người gọi khả năng thực hiện nhiều nhất có thể với đối tượng được trả về, ngay cả khi nó được ép dưới dạng giao diện. Tuy nhiên, điều này làm cho phương thức trở nên mỏng manh hơn, vì bất kỳ thay đổi nào đối với loại đối tượng được trả về có thể phá vỡ mã gọi. Tuy nhiên, trong thực tế, đó thường không phải là một vấn đề lớn.


1

Bạn chấp nhận Giao diện làm tham số cho một phương thức vì điều đó cho phép người gọi gửi các kiểu cụ thể khác nhau làm đối số. Với phương thức ví dụ LogAllChecked của bạn, tham số someClasses có thể có nhiều kiểu khác nhau và đối với người viết phương thức, tất cả đều có thể tương đương (tức là bạn viết cùng một mã chính xác bất kể loại tham số là gì). Nhưng đối với người gọi phương thức, nó có thể tạo ra sự khác biệt lớn - nếu họ có một mảng và bạn đang yêu cầu một danh sách, họ phải thay đổi mảng thành một danh sách hoặc vv bất cứ khi nào gọi phương thức, hoàn toàn lãng phí thời gian từ cả một lập trình viên và POV hiệu suất.

Việc bạn trả về Giao diện hay kiểu cụ thể phụ thuộc vào những gì bạn muốn cho phép người gọi của mình thực hiện với đối tượng bạn đã tạo - đây là một quyết định thiết kế API và không có quy tắc cứng và nhanh. Bạn phải cân nhắc giữa khả năng sử dụng đầy đủ của đối tượng với khả năng dễ dàng sử dụng một phần chức năng của đối tượng (và tất nhiên là bạn CÓ MUỐN chúng sử dụng đầy đủ đối tượng hay không). Ví dụ: nếu bạn trả về IEnumerable, thì bạn đang giới hạn chúng trong việc lặp lại - chúng không thể thêm hoặc xóa các mục khỏi đối tượng của bạn, chúng chỉ có thể hoạt động chống lại các đối tượng. Nếu bạn cần hiển thị một bộ sưu tập bên ngoài một lớp, nhưng không muốn để người gọi thay đổi bộ sưu tập, đây là một cách để thực hiện. Mặt khác, nếu bạn đang trả lại một bộ sưu tập trống mà bạn mong đợi / muốn chúng điền vào,


0

Đây là câu trả lời của tôi trong thế giới .NET 4.5+ này .

Sử dụng IList <T> và IReadonlyList <T> ,
              thay vì List <T> , vì ReadonlyList <T> không tồn tại.

IList <T> trông rất nhất quán với IReadonlyList <T>

  • Sử dụng IEnumerable <T> cho mức phơi nhiễm tối thiểu (thuộc tính) hoặc yêu cầu (thông số) nếu foreach là cách duy nhất để sử dụng nó.
  • Sử dụng IReadonlyList <T> nếu bạn cũng cần hiển thị / sử dụng Bộ đếm và [] chỉ mục.
  • Sử dụng IList <T> nếu bạn cũng cho phép người gọi thêm / cập nhật / xóa phần tử

bởi vì List <T> thực hiện IReadonlyList <T> , nó không cần bất kỳ quá trình truyền rõ ràng nào.

Một lớp mẫu:

// manipulate the list within the class
private List<int> _numbers;

// callers can add/update/remove elements, but cannot reassign a new list to this property
public IList<int> Numbers { get { return _numbers; } }

// callers can use: .Count and .ReadonlyNumbers[idx], but cannot add/update/remove elements
public IReadOnlyList<int> ReadonlyNumbers { get { return _numbers; } }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookie và Chính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.