Truyền danh sách <T> sang danh sách <Giao diện>


110
public interface IDic
{
    int Id { get; set; }
    string Name { get; set; }
}
public class Client : IDic
{

}

Làm thế nào tôi có thể truyền List<Client>đến List<IDic>?

Câu trả lời:


250

Bạn không thể truyền nó (duy trì danh tính tham chiếu) - điều đó sẽ không an toàn. Ví dụ:

public interface IFruit {}

public class Apple : IFruit {}
public class Banana : IFruit {}

...

List<Apple> apples = new List<Apple>();
List<IFruit> fruit = apples; // Fortunately not allowed
fruit.Add(new Banana());

// Eek - it's a banana!
Apple apple = apples[0];

Bây giờ bạn có thể chuyển đổi a List<Apple>thành an IEnumerable<IFruit>trong .NET 4 / C # 4 do hiệp phương sai, nhưng nếu muốn có, List<IFruit>bạn phải tạo một danh sách mới . Ví dụ:

// In .NET 4, using the covariance of IEnumerable<T>
List<IFruit> fruit = apples.ToList<IFruit>();

// In .NET 3.5
List<IFruit> fruit = apples.Cast<IFruit>().ToList();

Nhưng điều này không giống như đúc danh sách ban đầu - bởi vì bây giờ có hai danh sách riêng biệt . Điều này là an toàn, nhưng bạn cần hiểu rằng các thay đổi được thực hiện cho một danh sách sẽ không được nhìn thấy trong danh sách kia. ( Tất nhiên sẽ thấy các sửa đổi đối với các đối tượng mà danh sách đề cập đến.)


6
giống như họ vẫn đang ở trong tòa nhà!
Andras Zoltan

1
Sự khác biệt giữa thực hiện danh sách hiệp phương sai và thực hiện Danh sách mới <IFruit> (); sau đó lướt qua danh sách ban đầu và thêm từng mục vào danh sách IFruit? Nói cách khác ... tham chiếu đối tượng sẽ giống nhau, đúng không? Vì vậy, ... nếu điều đó là sự thật, cá nhân tôi không có ý nghĩa gì khi bạn không thể chỉ trực tiếp chọn toàn bộ danh sách. ?
Robert Noack

3
@RobertNoack: Bạn hiểu "tham chiếu đối tượng" nghĩa là gì? Tham chiếu đối tượng của mỗi phần tử là giống nhau, nhưng điều đó không giống với việc truyền tham chiếu danh sách. Giả sử bạn có thể ép kiểu tham chiếu, để bạn có một tham chiếu kiểu thời gian biên dịch List<IFruit>thực sự là tham chiếu đến a List<Apple>. Điều gì bạn mong đợi sẽ xảy ra nếu bạn thêm một Bananatham chiếu vào đó List<IFruit>?
Jon Skeet

1
Oh. Tôi nghĩ tôi cần học cách đọc. Tôi nghĩ câu trả lời ban đầu nói rằng các đối tượng trong danh sách sẽ được sao chép và tạo lại sâu, điều này không có ý nghĩa đối với tôi. Nhưng rõ ràng tôi đã bỏ lỡ phần chỉ tạo danh sách mới với các đối tượng giống nhau.
Robert Noack

2
@ TrươngQuốcKhánh: Tôi không có ý tưởng những gì bất kỳ mã vẻ của bạn như thế nào, hoặc cho dù bạn có một usingchỉ thị cho System.Linq, hoặc những gì bạn đang cố gắng gọi nó về, tôi không chắc chắn làm thế nào bạn mong đợi tôi để có thể giúp đỡ. Tôi khuyên bạn nên nghiên cứu thêm, và nếu bạn vẫn gặp khó khăn, bạn hãy đặt một câu hỏi với một ví dụ có thể tái tạo tối thiểu .
Jon Skeet,

6

Một trình lặp Cast và .ToList ():

List<IDic> casted = input.Cast<IDic>().ToList() sẽ thực hiện thủ thuật.

Ban đầu tôi đã nói hiệp phương sai sẽ hoạt động - nhưng như Jon đã chỉ ra một cách đúng đắn; không nó sẽ không!

Và ban đầu tôi cũng đã bỏ ToList()cuộc gọi một cách ngu ngốc


Casttrả về một IEnumerable<T>, không phải a List<T>- và không, hiệp phương sai sẽ không cho phép chuyển đổi này, vì nó sẽ không an toàn - hãy xem câu trả lời của tôi.
Jon Skeet

Từ trang bạn liên quan đến: "Chỉ có giao diện loại và các loại đại biểu có thể có các tham số kiểu biến thể"
Jon Skeet

@Jon - Tôi đã nhận ra sự ToList()thiếu sót trước khi đọc bình luận của bạn; nhưng có như bạn đã trình bày tất nhiên Covariance sẽ không hoạt động! Doh!
Andras Zoltan

Đúng. Covariance vẫn có thể hữu ích, vì nó có nghĩa là bạn không cần lệnh Castgọi trong .NET 4, miễn là bạn chỉ định đối số kiểu ToList.
Jon Skeet

5

Tôi cũng gặp vấn đề này và sau khi đọc câu trả lời của Jon Skeet, tôi đã sửa đổi mã của mình từ sử dụng List<T>sang sử dụng IEnumerable<T>. Mặc dù điều này không trả lời câu hỏi ban đầu của OP về Làm thế nào tôi có thể truyền List<Client>tớiList<IDic> , nhưng nó tránh được sự cần thiết phải làm như vậy và do đó có thể hữu ích cho những người khác gặp phải vấn đề này. Tất nhiên, điều này giả định rằng mã yêu cầu sử dụng List<IDic>nằm trong tầm kiểm soát của bạn.

Ví dụ:

public void ProcessIDic(IEnumerable<IDic> sequence)
{
   // Implementation
}

Thay vì:

public void ProcessIDic(List<IDic> list)
{
   // Implementation
}

3

Nếu bạn có thể sử dụng LINQ thì bạn có thể làm điều này ...

List<Client> clientList = new List<Client>();
List<IDic> list = clientList.Select(c => (IDic)c).ToList();

3
List<Client> listOfA = new List<Client>();
List<IDic> list = listOfA.Cast<IDic>().ToList();

0

Chỉ có thể bằng cách tạo mới List<IDic>và chuyển giao tất cả các yếu tố.


Bất kỳ nhận xét nào tại sao lại phản đối, vì ý nghĩa chung giống với tất cả các câu trả lời khác?
Piotr Auguscik

Tôi đoán sẽ được bạn đã downvoted bởi vì bạn nói nó chỉ có thể tạo ra một danh sách mới, nhưng một số khác đã được đưa lên ngược lại ... không tôi downvote dù)
musefan

Họ tạo danh sách mới nhưng không tạo với toán tử mới, điều này không thay đổi thực tế là họ làm.
Piotr Auguscik

0

Trong .Net 3.5, bạn có thể làm như sau:

List<ISomeInterface> interfaceList = new List<ISomeInterface>(list.Cast<ISomeInterface>());

Hàm tạo cho Danh sách trong trường hợp này lấy IEnumerable. mặc dù
danh sách chỉ có thể chuyển đổi thành IEnumerable. Mặc dù myObj có thể chuyển đổi thành ISomeInterface nhưng loại IEnumerable không thể chuyển đổi thành IEnumerable.


Vấn đề là điều này tạo ra một bản sao của danh sách, hầu hết thời gian bạn muốn thực hiện các thao tác trên danh sách gốc
cuộn vào
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 cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.