Connecting Tech Pros Worldwide Help | Site Map

Explicit specification of range variable required when hiding inherited IEnumerable<T>

Anders Borum
Guest
 
Posts: n/a
#1: Sep 5 '08
Hello

I'm developing an API with a number of concrete collections that inherit
from an abstract collection hierarchy. Each inheritance level provides an
implementation of IEnumerable<T>, thus allowing the developer to take full
advantage of the LINQ extensions without having to start with a cast
operation to a concrete type that matches the collection.

I was surprised that the C# compiler was unable to resolve the type of the
range variable when writing LINQ queries against a collection that hides an
inherited IEnumerable<T>. Consider the following example (just to be clear -
I'm fully aware that it is possible to write the following using a generic
collection instead, but please follow me);

public class Animal {}
public class Tiger : Animal {}

public abstract class AnimalCollection : IEnumerable<Animal>
{
public IEnumerator<AnimalGetEnumerator()
{
throw new NotImplementedException();
}

IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}

public class TigerCollection : AnimalCollection, IEnumerable<Tiger>
{
public new IEnumerator<TigerGetEnumerator()
{
throw new NotImplementedException();
}
}

public void ProcessAnimals()
{
AnimalCollection animals = new TigerCollection();
TigerCollection tigers = new TigerCollection();

// works
var a1 = from a in animals select a;

// fails (requires explicit specification of t as Tiger)
var t1 = from t in tigers select t;

// works when specifying Tiger explicitly
var t1 = from Tiger t in tigers select t;
}

Error 1 Could not find an implementation of the query pattern for source
type 'TigerCollection'. 'Select' not found. Consider explicitly specifying
the type of the range variable 't'.

We were just wondering why the C# compiler is unable to resolve the type
Tiger from the TigerCollection (now that it explicit implements
IEnumerable<Tiger>.

--
With regards
Anders Borum / SphereWorks
Microsoft Certified Professional (.NET MCP)

Marc Gravell
Guest
 
Posts: n/a
#2: Sep 5 '08

re: Explicit specification of range variable required when hiding inherited IEnumerable<T>


From the compiler's perspective, it has both IEnumerable<Tigerand
IEnumerable<Animalto pick from, and neither is "better", so it can't
make the decision on its own. Similarly, if you declare:

static void Test(IEnumerable<Animalanimals) {}
static void Test(IEnumerable<Tigertigers) {}

and call Test(tigers);

The call is ambiguous between the following methods or properties:
'Program.Test(System.Collections.Generic.IEnumerab le<Animal>)' and
'Program.Test(System.Collections.Generic.IEnumerab le<Tiger>)'

but add a static void Test(TigerCollection tigers) { } and it can do
it.

But it tells you how to fix it (and it is easy to do so), which is
usually enough...

Marc


Marc Gravell
Guest
 
Posts: n/a
#3: Sep 5 '08

re: Explicit specification of range variable required when hiding inherited IEnumerable<T>


To clarify - it *would* be able to pick a better candidate if the
difference was between Foo<Tand Bar<Twhere Foo<T: Bar<T>, but
this is very different to SomeType<Foovs SomeType<Barwhere Foo :
Bar.

Gotta love variance ;-p

Marc
Anders Borum
Guest
 
Posts: n/a
#4: Sep 5 '08

re: Explicit specification of range variable required when hiding inherited IEnumerable<T>


From the compiler's perspective, it has both IEnumerable<Tigerand
Quote:
IEnumerable<Animalto pick from, and neither is "better".
Well, I'm explicit asking for the enumerable object of IEnumerable<Tiger>. I
guess this one should be better than defaulting to the IEnumerable<Animal>.

--
With regards
Anders Borum / SphereWorks
Microsoft Certified Professional (.NET MCP)

Anders Borum
Guest
 
Posts: n/a
#5: Sep 5 '08

re: Explicit specification of range variable required when hiding inherited IEnumerable<T>


To clarify - it *would* be able to pick a better candidate if the
Quote:
difference was between Foo<Tand Bar<Twhere Foo<T: Bar<T>, but
this is very different to SomeType<Foovs SomeType<Barwhere Foo :
Bar.
Don't get me started on variance ;-)

--
With regards
Anders Borum / SphereWorks
Microsoft Certified Professional (.NET MCP)
Marc Gravell
Guest
 
Posts: n/a
#6: Sep 5 '08

re: Explicit specification of range variable required when hiding inherited IEnumerable<T>


Well, I'm explicit asking for the enumerable object of IEnumerable<Tiger>.

By adding the "Tiger"? yes.

Interestingly (maybe) this is a rare example of where knowing *less*
about a variable is better:

TigerCollection tigersOrig = new TigerCollection();
IEnumerable<Tigertigers = tigersOrig;
....
var t1 = from t in tigers select t;

Now the compiler doesn't have choices - it only knows about
IEnumerable<Tiger>, so it works.

Marc
Closed Thread