views:

150

answers:

2

I'm a bit stumped about how to perform the necessary cast in the following:

public IList<IMyClass> Foo()
{
    IList<IMyClass> foo = SomeQuery(); 

    var result = foo.GroupBy(x => x.bar).Select(x => new MyClass()).ToList();

    // So now I have a List<MyClass> which needs casting as IList<IMyClass>

    return result;
}

using an explicit cast fails at run time. The error message isn't helpful but I'm guessing it maybe something to do with the GroupBy as I wouldn't normally expect to have any problem with something like this.

+3  A: 

It's nothing to do with GroupBy - it's because IList<T> is invariant. If you could cast a List<MyClass> to IList<IMyClass> then the caller could then add an instance of a type which implemented IMyClass but wasn't a MyClass. (I usually think of this in terms of real world objects: a bunch of bananas isn't a fruit bowl: you can add an apple to a fruit bowl, but not to a bunch of bananas.)

The simplest way would be to manually specify the type in the Select clause:

public IList<IMyClass> Foo()
{
    IList<IMyClass> foo = SomeQuery(); 

    var result = foo.GroupBy(x => x.bar)
        .Select<IGrouping<IMyClass, Bar>>, IMyClass>(x => new MyClass())
        .ToList();
    return result;
}

This means result will actually be a List<IMyClass>. I don't know the type of item within foo which makes it tricky to give it in more detail - given your comment, I'll assume that that's IMyClass, and that x.bar is of type Bar.

Another alternative (which would be more useful if you wanted to use a query expression for the group/select part) would be to use Cast:

public IList<IMyClass> Foo()
{
    IList<IMyClass> foo = SomeQuery(); 

    var result = foo.GroupBy(x => x.bar)
                    .Select(x => new MyClass())
                    .Cast<IMyClass>()
                    .ToList();
    return result;
}

Or perform the cast within the Select lambda expression itself:

public IList<IMyClass> Foo()
{
    IList<IMyClass> foo = SomeQuery(); 

    var result = foo.GroupBy(x => x.bar)
                    .Select(x => (IMyClass) new MyClass())
                    .ToList();
    return result;
}

EDIT: Apologies for the Select failure. It's somewhat annoying that you have to specify both the source and the result, but there's no easy way of fixing that :( Given the ugliness involved, you may well be best off with Cast.

Jon Skeet
ah, never used the generic Select clause... see. lerned something new. thanks.
cRichter
@cRichter: `Enumerable.Select` is always a generic method - it's just that *usually* you let the compiler infer the type argument.
Jon Skeet
Hi Jon: I can't get the first example to compile. It says `Select` needs both a `TSource` and `TResult`, but when I try that I get an `IEnumerable<IGrouping<IMyClass>> doesn't contain a definition for Select` error. You might want to double check this part of your answer. I've got it working with `Cast` though, thanks.
fearofawhackplanet
@fearofawahckplannet: Oops - fixed.
Jon Skeet
+1  A: 

Do you use .net 4?

then its easy. just return an IEnumerable of IMyClass. that supports covariance, and therefore you can cast it to base types.

otherwise. do this.

result.Select(i=> (IMyClass)i).ToList();
cRichter
I would assume that the OP *wants* to return an `IList<T>` so that the caller can do list-y type things with it.
Jon Skeet
True. but... in fact. developers are the lazy kind of guys. so they are using normally the interface that is the easiest. and not that maybe would be the correct one^^
cRichter