Почему я не могу передать List <Customer>в качестве параметра методу, который принимает List <object>? - PullRequest
12 голосов
/ 02 декабря 2009

Следующий код дает мне эту ошибку:

Невозможно конвертировать из 'System.Collections.Generic.List' в 'System.Collections.Generic.List'.

Как я могу указать компилятору, что Customer действительно наследует от объекта? Или он просто не выполняет наследование с общими объектами коллекции (отправка List<string> получает ту же ошибку).

using System.Collections.Generic;
using System.Windows;
using System.Windows.Documents;

namespace TestControl3423
{
    public partial class Window2 : Window
    {
        public Window2()
        {
            InitializeComponent();

            List<Customer> customers = Customer.GetCustomers();
            FillSmartGrid(customers);

            //List<CorporateCustomer> corporateCustomers = CorporateCustomer.GetCorporateCustomers();
            //FillSmartGrid(corporateCustomers);
        }


        public void FillSmartGrid(List<object> items)
        {
            //do reflection on items and display dynamically
        }
    }

    public class Customer
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Street { get; set; }
        public string Location { get; set; }
        public string ZipCode { get; set; }

        public static List<Customer> GetCustomers()
        {
            List<Customer> customers = new List<Customer>();
            customers.Add(new Customer { FirstName = "Jim", LastName = "Jones", ZipCode = "23434" });
            customers.Add(new Customer { FirstName = "Joe", LastName = "Adams", ZipCode = "12312" });
            customers.Add(new Customer { FirstName = "Jake", LastName = "Johnson", ZipCode = "23111" });
            customers.Add(new Customer { FirstName = "Angie", LastName = "Reckar", ZipCode = "54343" });
            customers.Add(new Customer { FirstName = "Jean", LastName = "Anderson", ZipCode = "16623" });
            return customers;
        }
    }
}

Ответы [ 8 ]

14 голосов
/ 02 декабря 2009

.NET не имеет ко-дисперсии и противоречия (пока).

То, что B происходит от A, не означает, что List<B> происходит от List<A>. Это не так. Это два совершенно разных типа.

.NET 4.0 получит ограниченную ко-дисперсию и контр-дисперсию.

8 голосов
/ 02 декабря 2009

Это проблема ковариации, и это не так просто, как кажется на первый взгляд. C # 4 будет иметь некоторую поддержку для этого.

Чтобы получить представление о проблемах, представьте в вашем случае, что этот актерский состав действительно сработает. Теперь у вас есть List<object>, который, например, также имеет метод Add. Однако аргумент для фактического Add должен быть Customer, так что это явно нарушает реализацию; реализация не обеспечивает метод Add(object obj).

К сожалению, некоторые проблемы могли быть решены с помощью разумного (er) дизайна интерфейсов с универсальными методами, где ковариация в порядке, например, для GetEnumerator.

7 голосов
/ 02 декабря 2009

C # (в настоящее время) не поддерживает дисперсию для универсальных типов.

Однако, если вы используете C # 3.0, вы можете сделать это:

FillSmartGrid( customers.Cast<object>() );
6 голосов
/ 02 декабря 2009

Это потому, что список класса не может быть преобразован в список базового класса. Это осознанное решение сделать язык безопасным. Этот вопрос часто задают.

Здесь - это мой стандартный ответ, как обойти проблему:

List<A> listOfA = new List<C>().ConvertAll(x => (A)x);

или

List<A> listOfA = new List<C>().Cast<A>().ToList();

Также здесь действительно хорошее объяснение от самого Эрика Липперта, одного из главных архитекторов команды C #.

2 голосов
/ 02 декабря 2009

Это было покрыто до тошноты во многих других ответах. Краткий ответ таков:

Считайте, что у меня есть эти переменные:

List<Customer> customers = new List<Customer>(); //ok, seems fair
List<object> objects = new List<object>(); // again, everything's fine

Теперь вот где он перестает компилироваться:

objects = customers; // sounds OK, since a Customer is an object, right?

objects.Add("foo"); 

Имеет ли это смысл сейчас?

C # 4.0 предоставит ограниченную способность делать то, что вы пытаетесь, хотя возможность делать точно , как вы описываете (присвоение List<Customer> для List<object> не будет разрешено для Те же рассуждения я изложил выше). См. блог Эрика Липперта для получения дополнительной информации и исправления к некоторой дезинформации, которая происходит вокруг паутины.

Чтобы ответить на ваш комментарий выше, нет причины, по которой вы не можете выполнять те же операции отражения для экземпляра Customer, что и для object.

1 голос
/ 03 декабря 2009

Вместо передачи List, который не работает по вышеуказанным причинам, не могли бы вы просто передать просто ссылку на объект, а затем получить тип списка, вроде ...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1 {

    class Customer {
        public int id;
        public string name;
    }

    class Monkey {

        public void AcceptsObject(object list) {

            if (list is List<Customer>) {
                List<Customer> customerlist = list as List<Customer>;
                foreach (Customer c in customerlist) {
                    Console.WriteLine(c.name);
                }
            }
        }
    }

    class Program {
        static void Main(string[] args) {

            Monkey monkey = new Monkey();
            List<Customer> customers = new List<Customer> { new Customer() { id = 1, name = "Fred" } };
            monkey.AcceptsObject(customers);
            Console.ReadLine();
        }
    }
}
0 голосов
/ 03 декабря 2009

Я согласен с ответом Уинстона Смита.

Я просто хотел указать в качестве альтернативы (хотя это, возможно, не лучший способ справиться с ситуацией), что хотя List<Customer> не является производным от List<Object>, Customer [] происходит от Object [].

Следовательно, возможно сделать это:

    {
        List<Customer> customers = new List<Customer>();
        // ...
        FillSmartGrid(customers.ToArray());
        // ...
    }

    public void FillSmartGrid(object[] items)
    {
    }

Конечно, недостатком является то, что вы создаете объект массива только для того, чтобы вы могли передать его этой функции.

0 голосов
/ 03 декабря 2009

Почему параметр не может иметь тип IList?

public void FillSmartGrid (элементы IList)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...