Ответ 1
У .NET нет ковариантности и противоречия (пока).
То, что B происходит от A, не означает, что List<B>
происходит от List<A>
. Это не так. Это два совершенно разных типа.
.NET 4.0 получит ограниченную совместную дисперсию и противоречие.
Следующий код дает мне эту ошибку:
Невозможно преобразовать из 'System.Collections.Generic.List' в 'System.Collections.Generic.List'.
Как я могу указать компилятору, что клиент действительно наследует объект? Или он просто не наследует общие объекты коллекции (отправка 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;
}
}
}
У .NET нет ковариантности и противоречия (пока).
То, что B происходит от A, не означает, что List<B>
происходит от List<A>
. Это не так. Это два совершенно разных типа.
.NET 4.0 получит ограниченную совместную дисперсию и противоречие.
Это проблема ковариации, и это не так просто, как кажется на первый взгляд. С# 4 будет иметь некоторую поддержку для этого.
Чтобы получить представление о проблемах, представьте в вашем случае, что этот актер действительно будет работать. Теперь у вас есть List<object>
, который, например, также имеет метод Add
. Однако аргумент для фактического Add
должен быть Customer
, так что это явно нарушает реализацию; реализация не предоставляет метод Add(object obj)
.
К сожалению, некоторые проблемы могут быть решены с помощью интеллектуального (er) дизайна интерфейсов с помощью общих методов, где ковариация в порядке, например, для GetEnumerator.
С# (в настоящее время) не поддерживает дисперсию для общих типов.
Однако, если вы используете С# 3.0, вы можете сделать это:
FillSmartGrid( customers.Cast<object>() );
Это потому, что список класса не конвертируется в список базового класса. Это преднамеренное решение сделать язык безопасным. Этот вопрос часто задают.
Здесь - мой стандартный ответ о том, как обойти эту проблему:
List<A> listOfA = new List<C>().ConvertAll(x => (A)x);
или
List<A> listOfA = new List<C>().Cast<A>().ToList();
Также здесь - действительно хорошее объяснение самого Эрика Липперта, одного из главных архитекторов команды С#.
Это было затронуто ad-nauseum во многих других ответах. Короткий ответ таков:
У меня есть следующие переменные:
List<Customer> customers = new List<Customer>(); //ok, seems fair
List<object> objects = new List<object>(); // again, everything fine
Теперь, где он прекращает компиляцию:
objects = customers; // sounds OK, since a Customer is an object, right?
objects.Add("foo");
Это имеет смысл сейчас?
С# 4.0 будет вводить ограниченную возможность делать то, что вы пытаетесь сделать, хотя возможность делать то, что вы описываете (назначить List<Customer>
на List<object>
не будет разрешено по тем же соображениям, которые я изложил выше), См. блог Эрика Липперта для получения дополнительной информации и исправление некоторой дезинформации, которая происходит вокруг межсетевых экранов.
Чтобы ответить на ваш комментарий выше, нет причин, по которым вы не можете выполнять одни и те же операции отражения в экземпляре Customer
, как вы можете на object
.
Вместо того, чтобы передавать 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();
}
}
}
Почему параметр не может иметь тип IList?
public void FillSmartGrid (элементы IList)
Я согласен с ответом Уинстона Смита.
Я просто хотел указать в качестве альтернативы (хотя это, возможно, не лучший способ справиться с ситуацией), что, хотя List<Customer>
не получается из List<Object>
, Customer [] выводит объект Object [].
Следовательно, это можно сделать:
{
List<Customer> customers = new List<Customer>();
// ...
FillSmartGrid(customers.ToArray());
// ...
}
public void FillSmartGrid(object[] items)
{
}
Конечно, недостатком является то, что вы создаете объект массива, чтобы передать его этой функции.