views:

80

answers:

2

I have an abstract class called Grouping. I have a subclass called GroupingNNA.

public class GroupingNNA : Grouping {
  // blah blah blah
}

I have a List that contains items of type GroupingNNA, but is actually declared to contain items of type Grouping.

List<Grouping> lstGroupings = new List<Grouping>();
lstGroupings.Add(
  new GroupingNNA { fName = "Joe" });
lstGroupings.Add(
  new GroupingNNA { fName = "Jane" });

The Problem: The following LINQ query blows up on me because of the fact that lstGroupings is declared as List< Grouping> and fName is a property of GroupingNNA, not Grouping.

var results = from g in lstGroupings
              where r.fName == "Jane"
              select r;

Oh, and this is a compiler error, not a runtime error. Thanks in advance for any help on this one!

More Info: Here is the actual method that won't compile. The OfType() fixed the LINQ query, but the compiler doesn't like the fact that I'm trying to return the anonymous type as a List< Grouping>.

private List<Grouping> ApplyFilterSens(List<Grouping> lstGroupings, string fSens) {

  // This works now! Thanks @Lasse
  var filtered = from r in lstGroupings.OfType<GroupingNNA>()
                 where r.QASensitivity == fSens
                 select r;

  if (filtered != null) {
    **// Compiler doesn't like this now**
    return filtered.ToList<Grouping>();
  }
  else
    return new List<Grouping>();
  }
+4  A: 

Try:

= from g in lstGroupings.OfType<GroupingNNA>()

this will skip any elements not of type GroupingNNA, and also make the compiler use GroupingNNA as the type for g.

In response to comment and edited question. No, the compiler will certainly not be happy about your changed collection, but you can fix that:

return new List<Grouping>(filtered.ToArray());

This relies on the fact that arrays in .NET are co/contra-variant, which allows the compiler to treat GroupingNNA[] as Grouping[] for the constructor.

Also, you don't need the if (filtered != null) check, you will get a collection in filtered, it might just not produce any elements, but filtered will always be non-null.

This means your code can be written as:

var filtered = from r in lstGroupings.OfType<GroupingNNA>()
               where r.QASensitivity == fSens
               select r;
return new List<Grouping>(filtered.ToArray());

or even just:

return new List<Grouping>((from r in lstGroupings.OfType<GroupingNNA>()
                           where r.QASensitivity == fSens
                           select r).ToArray());

or even shorter if you drop the linq syntax:

return new List<Grouping>((lstGroupings.OfType<GroupingNNA>()
    .Where(r => r.QASensitivity == fSens).ToArray());

Note that you can of course use OfType to go the other way as well:

return filtered.OfType<Grouping>().ToList();

there shouldn't be any big performance differences between the different ways here, if in doubt, measure it, but I would go with what you find easiest to read and understand.

Lasse V. Karlsen
Thank you! I'm one step closer now. However, the compiler isn't happy now because I'm trying to return the anonymous type as a List< Grouping>. I'll make an edit to my original post with some more code so you can see what I'm talking about. Thanks again.
Jagd
+4  A: 

Calling ToList<AnotherType>() is putting yourself back in the same boat as before - generic covariance/contravarience is not supported - e.g. List<Type> is not compatible with or convertible to/from List<SubType>.

You need to use filtered.OfType<Grouping>().ToList(); - the first gets you back to an IEnumerable<Grouping> instead of an IEnumerable<GroupingNNA>, and then ToList just converts the IEnumerable to a List of the same generic type.

Rex M
.NET 2.x through 3.5 Generic Lists are invarient. This is for your own good. Look to .NET 4.0 for support of more loose typing in generics
Chris Ballance