tags:

views:

295

answers:

8

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;
        }
    }
}
+7  A: 

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>() );
Winston Smith
+1 for this. If you are pre-version-3 then you can write a method to convert the List<Customer> into a List<Object>.
Sohnee
+9  A: 

.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.

Mark Seemann
I don't think the covariance in .NET 4 will help in this situation.
Mark Byers
@Mark Byers: why? looks like it should help a lot
Randolpho
@Mark is correct. The covariance support being added to C# is limited to interfaces and delegates. List<T> is a concrete type and cannot take advantage here
JaredPar
There is no covariance for lists, as it is neither "in" nor "out"
Marc Gravell
See also: http://marcgravell.blogspot.com/2009/02/what-c-40-covariance-doesn-do.html
Marc Gravell
And actually, the CLI *does* have this - *C#* doesn't, except for arrays.
Marc Gravell
I may have fired off my answer a bit too soon. Should I delete or edit my post?
Mark Seemann
+6  A: 

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.

Lucero
+4  A: 

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.

Mark Byers
+1  A: 

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.

Adam Robinson
A: 

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();
        }
    }
}
Dan Hedges
A: 

Why can't the parameter be of type IList?

public void FillSmartGrid(IList items)

ScottE
A: 

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.

Dr. Wily's Apprentice