tags:

views:

202

answers:

5

It was suggested in this question, that I could cast a generic collection upward to a collection of objects with .Cast<object>. After reading up a bit on .Cast<>, I still can't get it a generic collection to cast into another generic collection. Why doesn't the following work?

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

namespace TestCast2343
{
    class Program
    {
        static void Main(string[] args)
        {
            List<string> strings = new List<string> { "one", "two", "three" };

            //gives error: cannot convert from 'System.Collections.Generic.List<string>'
            //to 'System.Collections.Generic.List<object>'
            //IEnumerable<string> items = strings.Cast<object>();

            //this works
            strings.Cast<object>();

            //but they are still strings:
            foreach (var item in strings)
            {
                System.Console.WriteLine(item.GetType().Name);
            }

            //gives error: cannot convert from 'System.Collections.Generic.List<string>'
            //to 'System.Collections.Generic.List<object>'
            ProcessCollectionDynamicallyWithReflection(strings);

            Console.ReadLine();
        }

        static void ProcessCollectionDynamicallyWithReflection(List<object> items)
        {
            //...
        }
    }

}

Answer:

Thank you Reed, here's the code I got to work:

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

namespace TestCast2343
{
    class Program
    {
        static void Main(string[] args)
        {
            List<string> strings = new List<string> { "one", "two", "three" };
            List<int> ints = new List<int> { 34, 35, 36 };
            List<Customer> customers = Customer.GetCustomers();

            ProcessCollectionDynamicallyWithReflection(strings.Cast<object>().ToList());
            ProcessCollectionDynamicallyWithReflection(ints.Cast<object>().ToList());
            ProcessCollectionDynamicallyWithReflection(customers.Cast<object>().ToList());

            Console.ReadLine();
        }

        static void ProcessCollectionDynamicallyWithReflection(List<object> items)
        {
            foreach (var item in items)
            {
                Console.WriteLine(item.GetType().Name);
            }
        }
    }

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

It's because the Cast<> method does not return a List<T> type but instead an IEnumerable<T>. Add a .ToList call to the end and it will fix the problem.

strings.Cast<object>().ToList();
JaredPar
@Downvoter care to explain?
JaredPar
+11  A: 

You're misusing Cast<T>.

First, here:

IEnumerable<string> items = strings.Cast<object>();

When you call strings.Cast<object>(), this will return IEnumerable<object>, not IEnumerable<string>. However, the items in the collection are still strings, but being held in references to objects.

Later, when you want to pass this into a method that takes a List<object>, you need to turn your IEnumerable<T> into an IList<T>. This could easily be done like so:

// Cast to IEnumerabe<object> then convert to List<object>
ProcessCollectionDynamicallyWithReflection(strings.Cast<object>().ToList());
Reed Copsey
Mesleading: The code does NOT turn the `IEnumerable<object>` into a `List<object>`, it creates a *new* `List` with the items from the `IEnumerable`.
Guffa
Yes. It's not a cast, it a full conversion. Enumerable.Cast<T> is similar, though - again, not just a single casting operation, but rather generating a new enumerable sequence.
Reed Copsey
A: 

This should work:

IEnumerable<object> items = strings.Cast<object>();

Also, you can't pass an IEnumerable as a List into the ProcessCollectionDynamicallyWithReflection without a cast. So, this is even better:

List<object> items = strings.Cast<object>().ToList();
John Buchanan
A: 

It was suggested in this question, that I could cast a generic collection upward to a collection of objects with .Cast<object>

No, that is not correct. The Cast method doesn't cast the collection, it casts each item in the collection, returning a new collection containing the cast values.

You are creating the collection of object, but then you just throw it away as you are not assigning it to a variable. (Well, you are actually throwing away an expression that is capable of creating the collection, but that is not so relevant.)

You need to assign the collection to a variable to keep it, and as you need a List<object> you need to put the cast items in a list:

List<object> objects = strings.Cast<object>.ToList();

Perhaps you should consider using generics instead of casting to object:

static void ProcessCollectionDynamicallyWithReflection<T>(List<T> items)

That way you can write strongly typed code in the method, and you don't have to create a new collection just to be able to send it to the method.

Guffa
+1  A: 

You can also address the casting problem from another perspective: fixing ProcessCollectionDynamicallyWithReflection since it's unnecessarily restrictive:

private static void ShowItemTypes(IEnumerable items)
{
    foreach (object item in items)
    {
        string itemTypeName = (item != null) ? item.GetType().Name : "null";
        Console.WriteLine(itemTypeName);
    }
}
280Z28