Colin Bacon
Colin Bacon

Reputation: 15619

InvalidCastException: Unable cast generic List to object

Could someone please explain to me why the following cast does not work and the solution to the problem.

I have a GroupedResult:

public class GroupedResult<TKey, TElement>
{
    public TKey Key { get; set; }

    private readonly IEnumerable<TElement> source;

    public GroupedResult(TKey key, IEnumerable<TElement> source)
    {
        this.source = source;
        this.Key = key;
    }
}

public class Bacon 
{
}

I would like to cast the List<string, Bacon> to List<string, object>. I have tried the following and other ways.

var list = new List<GroupedResult<string, Bacon>>
    {
        new GroupedResult<string, Bacon>("1", new List<Bacon>()),
        new GroupedResult<string, Bacon>("2", new List<Bacon>())
    };

var result = list.Cast<GroupedResult<string, object>>().ToList();

But I always get the following error:

InvalidCastException: Unable to cast object of type 'GroupedResult2[System.String,UserQuery+Bacon]' to type 'GroupedResult2[System.String,System.Object]'.

Upvotes: 1

Views: 1634

Answers (4)

juharr
juharr

Reputation: 32296

For that to work you'd have to use an interface instead of class type.

public interface IGroupResult<TKey, out TElement>
{
    TKey Key { get; set; }
}

public class GroupedResult<TKey, TElement> : IGroupResult<TKey, TElement>
{
    public TKey Key { get; set; }

    private readonly IEnumerable<TElement> source;

    public GroupedResult(TKey key, IEnumerable<TElement> source)
    {
        this.source = source;
        this.Key = key;
    }
}

public class Bacon
{

}

Then you could do something like

IGroupResult<string, Bacon> g = new GroupedResult<string, Bacon>("1", new List<Bacon>());

var result = (IGroupResult<string, object>)g;

That's because co-variance is only allowed on interfaces and delegates, but not classes. Note that you should only mark a type as co-variant if it only comes out of the interface (method return type and read only properties).

Though you should ask yourself why you want to cast something to object when you're working with generics. The main point of generics is to avoid having to use the object type as a catch all and this could indicate a flaw in your design that you might want to rethink.

Upvotes: 1

Kahbazi
Kahbazi

Reputation: 15015

You can have a Cast method in your GroupedResult class and use it to do the casting!

public class GroupedResult<TKey, TElement>
{
    public TKey Key { get; set; }

    private readonly IEnumerable<TElement> source;

    public GroupedResult(TKey key, IEnumerable<TElement> source)
    {
        this.source = source;
        this.Key = key;
    }

    public GroupedResult<TKey, object> Cast()
    {
        return new GroupedResult<TKey, object>(Key, source.Cast<object>());
    }
}

public class Bacon
{ 
}

static void Main(string[] args)
{

   var list = new List<GroupedResult<string, Bacon>>
   {
        new GroupedResult<string, Bacon>("1", new List<Bacon>()),
        new GroupedResult<string, Bacon>("2", new List<Bacon>())
    };

    // var result = list.Cast<GroupedResult<string, object>>().ToList();
    List<GroupedResult<string,object>> result = list.Select(B => B.Cast()).ToList();
}

Upvotes: 0

Ashkan Mobayen Khiabani
Ashkan Mobayen Khiabani

Reputation: 34180

why aren'y you using GroupedResult<string, object> instead of GroupedResult<string, Bacon>? like this:

var list = new List<GroupedResult<string, object>>
    {
        new GroupedResult<string, object>("1", new List<Bacon>()),
        new GroupedResult<string, object>("2", new List<Bacon>())
    };

Upvotes: 0

Paul Swetz
Paul Swetz

Reputation: 2254

It would be better to start with a GroupedResult < string, object> then you COULD do this

new GroupedResult<string, object>("2", new List<Bacon>())

Upvotes: 0

Related Questions