Tutorial :Why can't I pass List<Customer> as a parameter to a method that accepts List<object>?



Question:

The following code gives me this error:

Cannot convert from 'System.Collections.Generic.List' to 'System.Collections.Generic.List'.

How can I indicate to the compiler that Customer indeed inherits from object? Or does it just not do inheritance with generic collection objects (sending a List<string> gets the same error).

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;          }      }  }  


Solution:1

.NET does not have co-variance and contra-variance (yet).

That B derives from A doesn't imply that List<B> derives from List<A>. It doesn't. They are two totally different types.

.NET 4.0 will get limited co-variance and contra-variance.


Solution:2

That's the problem of covariance, and it is not as easy as it looks at first sight. C# 4 will have some support for that.

To get the idea of the problems, imagine in your case that this cast would actually work. Now you'h have a List<object>, which for instance also has an Add method. However, the argument for the actual Add must be a Customer, so that this clearly violates the implementation; the implementation does not provide the Add(object obj) method.

Unfortunately, some issues could have been solved by using a smart(er) design of the interfaces with generic methods where covariance is OK, such as for GetEnumerator.


Solution:3

C# (at present) does not support variance for generic types.

However, if you're using C# 3.0, you can do this:

FillSmartGrid( customers.Cast<object>() );  


Solution:4

It's because a list of a class is not convertible to a list of the base class. It's a deliberate decision to make the language safe. This question gets asked often.

Here is my standard answer of how to work around the issue:

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

or:

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

Also here is really good explanation from Eric Lippert himself, one of the chief architects on the C# team.


Solution:5

This has been covered ad-nauseum in many other answers. The short answer is this:

Consider I have these variables:

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

Now, here's where it stops compiling:

objects = customers; // sounds OK, since a Customer is an object, right?    objects.Add("foo");   

Does that make sense now?

C# 4.0 will introduce limited ability to do what you're attempting, though being able to do exactly as you describe (assign a List<Customer> to a List<object> won't be allowed for the same reasoning I outlined above). See Eric Lippert's blog for more information, and a correction to some misinformation that's going around the interwebs.

To address your comment above, there's no reason that you can't perform the same reflection operations on a Customer instance as you can on an object.


Solution:6

Instead of passing List which does not work for the reasons above, could you not simply pass just an object reference then get the list type afterwards, kinda like...

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();          }      }  }  


Solution:7

Why can't the parameter be of type IList?

public void FillSmartGrid(IList items)


Solution:8

I agree with Winston Smith's answer.

I just wanted to point out as an alternative (although this possibly isn't the best way to handle the situation) that while List<Customer> does not derive from List<Object>, Customer[] does derive from Object[].

Therefore, it is possible to do this:

    {          List<Customer> customers = new List<Customer>();          // ...          FillSmartGrid(customers.ToArray());          // ...      }        public void FillSmartGrid(object[] items)      {      }  

Of course, the downside is that you're creating an array object just so you can pass it to this function.


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »