Trong C #, tại sao một đối tượng List <string> không thể được lưu trữ trong một biến List <object>


85

Có vẻ như một đối tượng Danh sách không thể được lưu trữ trong một biến Danh sách trong C #, và thậm chí không thể được truyền một cách rõ ràng theo cách đó.

List<string> sl = new List<string>();
List<object> ol;
ol = sl;

kết quả là Không thể chuyển đổi hoàn toàn loại System.Collections.Generic.List<string>thànhSystem.Collections.Generic.List<object>

Và sau đó...

List<string> sl = new List<string>();
List<object> ol;
ol = (List<object>)sl;

kết quả là Không thể chuyển đổi loại System.Collections.Generic.List<string>thànhSystem.Collections.Generic.List<object>

Tất nhiên, bạn có thể làm điều đó bằng cách kéo mọi thứ ra khỏi danh sách chuỗi và đặt chúng trở lại từng chuỗi một, nhưng đó là một giải pháp khá phức tạp.


5
Điều này sẽ thay đổi với C # 4.0, vì vậy bạn có thể muốn tra cứu hiệp phương sai và phương sai. Nó sẽ cho phép những thứ như vậy một cách an toàn.
John Leidegren

Nhiều hoặc ít trùng lặp: stackoverflow.com/questions/317335/…
Joel in Gö

7
John> C # 4 sẽ không cho phép điều này. Hãy nghĩ về ol.Add (new object ());
Guillaume

Nó trả lời của Jon Skeet đây: stackoverflow.com/a/2033921/1070906
JCH2k

Câu trả lời:


39

Hãy nghĩ theo cách này, nếu bạn thực hiện một phép ép kiểu như vậy, và sau đó thêm một đối tượng kiểu Foo vào danh sách, danh sách các chuỗi không còn nhất quán nữa. Nếu bạn lặp lại tham chiếu đầu tiên, bạn sẽ nhận được một ngoại lệ ép kiểu lớp vì một khi bạn nhấn vào cá thể Foo, Foo không thể được chuyển đổi thành chuỗi!

Một lưu ý phụ, tôi nghĩ việc bạn có thể làm ngược lại hay không sẽ quan trọng hơn:

List<object> ol = new List<object>();
List<string> sl;
sl = (List<string>)ol;

Tôi đã không sử dụng C # trong một thời gian, vì vậy tôi không biết liệu điều đó có hợp pháp hay không, nhưng kiểu diễn viên đó thực sự (có khả năng) hữu ích. Trong trường hợp này, bạn đang đi từ một lớp (đối tượng) tổng quát hơn sang một lớp cụ thể hơn (chuỗi) mở rộng từ lớp chung. Bằng cách này, nếu bạn thêm vào danh sách các chuỗi, bạn không vi phạm danh sách các đối tượng.

Có ai biết hoặc có thể kiểm tra xem dàn diễn viên như vậy có hợp pháp trong C # không?


4
Tôi nghĩ ví dụ của bạn cũng gặp phải vấn đề tương tự. Điều gì xảy ra nếu ol có một cái gì đó trong đó không phải là một chuỗi? Vấn đề là một số phương pháp trên Danh sách sẽ hoạt động tốt, chẳng hạn như thêm / chèn. Nhưng lặp đi lặp lại có thể là một vấn đề thực sự.
Chris Ammerman 30/09/08

3
Eric Lippert có một loạt bài đăng trên blog về chủ đề này: tại sao việc thêm hiệp phương sai và tương phản đối phương vào các phương pháp chung có thể hiệu quả, nhưng có thể không bao giờ hoạt động theo cách chúng ta muốn ở cấp lớp. is.gd/3kQc
Chris Ammerman 30/09/08

@ChrisAmmerman: Nếu olcó thứ gì đó trong đó không phải là một chuỗi, tôi cho rằng tôi sẽ mong đợi quá trình truyền lỗi trong thời gian chạy. Nhưng nơi bạn thực sự sẽ gặp rắc rối là nếu quá trình truyền thành công, và sau đó một cái gì đó được thêm vào olđó không phải là một chuỗi. Bởi vì sltham chiếu cùng một đối tượng, bây giờ của bạn List<string>sẽ chứa một chuỗi không phải là chuỗi. Các Addlà vấn đề, mà tôi đoán biện minh cho lý do tại sao mã này sẽ không biên dịch, nhưng nó sẽ biên dịch nếu bạn thay đổi List<object> olđể IEnumerable<object> ol, mà không có một Add. (Tôi đã kiểm tra điều này trong C # 4.)
Tim Goodman

Với thay đổi đó, nó biên dịch nhưng ném ra một InvalidCastExceptionvì kiểu thời gian chạy của olvẫn là List<object>.
Tim Goodman

36

Nếu bạn đang sử dụng .NET 3.5, hãy xem phương thức Enumerable.Cast. Đó là một phương thức mở rộng để bạn có thể gọi nó trực tiếp trên Danh sách.

List<string> sl = new List<string>();
IEnumerable<object> ol;
ol = sl.Cast<object>();

Đó không phải là chính xác những gì bạn yêu cầu nhưng nên thực hiện thủ thuật.

Chỉnh sửa: Theo ghi nhận của Zooba, sau đó bạn có thể gọi ol.ToList () để lấy Danh sách


14

Bạn không thể truyền giữa các kiểu chung với các tham số kiểu khác nhau. Các kiểu chung chung chuyên biệt không tạo thành một phần của cùng một cây kế thừa và các kiểu không liên quan cũng vậy.

Để làm điều này trước NET 3.5:

List<string> sl = new List<string>();
// Add strings to sl

List<object> ol = new List<object>();

foreach(string s in sl)
{
    ol.Add((object)s);  // The cast is performed implicitly even if omitted
}

Sử dụng Linq:

var sl = new List<string>();
// Add strings to sl

var ol = new List<object>(sl.Cast<object>());

// OR
var ol = sl.Cast<object>().ToList();

// OR (note that the cast to object here is required)
var ol = sl.Select(s => (object)s).ToList();

11

Lý do là một lớp chung chung List<>, đối với hầu hết các mục đích, được đối xử bên ngoài như một lớp bình thường. ví dụ: khi bạn nói List<string>()trình biên dịch nói ListString()(trong đó chứa các chuỗi). [Dân gian kỹ thuật: đây là một phiên bản tiếng Anh cực kỳ đơn giản của những gì đang xảy ra]

Do đó, rõ ràng là trình biên dịch không thể đủ thông minh để chuyển đổi ListString thành ListObject bằng cách truyền các mục của bộ sưu tập nội bộ của nó.

Đó là lý do tại sao có các phương thức mở rộng cho IEnumerable như Convert () cho phép bạn dễ dàng cung cấp chuyển đổi cho các mục được lưu trữ bên trong một bộ sưu tập, có thể đơn giản như truyền từ cái này sang cái khác.


6

Điều này liên quan rất nhiều đến hiệp phương sai, ví dụ: các kiểu chung được coi là tham số và nếu các tham số không phân giải đúng thành một kiểu cụ thể hơn thì hoạt động không thành công. Ý nghĩa của điều này là bạn thực sự không thể truyền đến một loại đối tượng chung chung hơn. Và như Rex đã nêu, đối tượng Danh sách sẽ không chuyển đổi từng đối tượng cho bạn.

Bạn có thể muốn thử mã ff thay thế:

List<string> sl = new List<string>();
//populate sl
List<object> ol = new List<object>(sl);

hoặc là:

List<object> ol = new List<object>();
ol.AddRange(sl);

ol sẽ (về mặt lý thuyết) sao chép tất cả nội dung của sl mà không gặp vấn đề gì.


5

Có, bạn có thể, từ .NET 3.5:

List<string> sl = new List<string>();
List<object> ol = sl.Cast<object>().ToList();



1

Điều đó thực sự để bạn không cố gắng đặt bất kỳ "đối tượng" kỳ lạ nào trong biến thể danh sách "ol" của mình ( List<object>dường như cho phép) - bởi vì khi đó mã của bạn sẽ bị lỗi (bởi vì danh sách thực sự đang List<string>và sẽ chỉ chấp nhận các đối tượng kiểu chuỗi ). Đó là lý do tại sao bạn không thể chuyển biến của mình thành một thông số kỹ thuật chung hơn.

Trên Java thì ngược lại, bạn không có các định dạng chung, và thay vào đó mọi thứ đều là Danh sách đối tượng trong thời gian chạy và bạn thực sự có thể nhồi bất kỳ đối tượng lạ nào vào Danh sách được đánh máy nghiêm ngặt của mình. Tìm kiếm "Chung quy được sửa đổi" để xem thảo luận rộng hơn về vấn đề của java ...


1

Hiệp phương sai như vậy trên generic không được hỗ trợ, nhưng bạn thực sự có thể làm điều này với các mảng:

object[] a = new string[] {"spam", "eggs"};

C # thực hiện kiểm tra thời gian chạy để ngăn bạn đưa intvào a.


0

Đây là một giải pháp pre-.NET 3.5 khác cho bất kỳ IList nào có nội dung có thể được truyền ngầm.

public IList<B> ConvertIList<D, B>(IList<D> list) where D : B
{
    List<B> newList = new List<B>();

    foreach (D item in list)
    {
        newList.Add(item);
    }

    return newList;
}

(Dựa trên ví dụ của Zooba)


0

Tôi có một:

private List<Leerling> Leerlingen = new List<Leerling>();

Và tôi sẽ điền vào nó với dữ liệu được thu thập trong một List<object> Điều cuối cùng đã làm việc cho tôi là cái này:

Leerlingen = (List<Leerling>)_DeserialiseerLeerlingen._TeSerialiserenObjecten.Cast<Leerling>();

.Castnó vào loại bạn muốn lấy IEnumerabletừ loại đó, sau đó nhập IEnemuerablevào loại List<>bạn muốn.


0

Mm, nhờ những bình luận trước đây, tôi đã tìm ra hai cách để tìm ra nó. Đầu tiên là lấy danh sách chuỗi các phần tử và sau đó truyền nó vào danh sách đối tượng IEnumerable:

IEnumerable<object> ob;
List<string> st = new List<string>();
ob = st.Cast<object>();

Và cách thứ hai là tránh kiểu đối tượng IEnumerable, chỉ cần truyền chuỗi thành kiểu đối tượng và sau đó sử dụng hàm "toList ()" trong cùng một câu:

List<string> st = new List<string>();
List<object> ob = st.Cast<object>().ToList();

Tôi thích cách thứ hai hơn. Tôi hi vọng cái này giúp được.


0
List<string> sl = new List<string>();
List<object> ol;
ol = new List<object>(sl);
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.