Ayende @ Rahien

Unnatural acts on source code

More generics gotchas, argh!

I have been working on .NET 2.0 for a long time now, generics still manage to trip me.

Consider this piece of code:

public interface IFoo<TFromInterface> { }

public class Foo<TFromClass> : IFoo<T> { }

[Test]
public void More_Generics_Gotcha()
{
	Assert.AreEqual(typeof(IFoo<>), 
		typeof(Foo<>).GetInterfaces()[0]);
}

This test fails. the returned System.Type instance is a IFoo<TFromClass>, which is  an unbounded generic parameter, but not the same as IFoo<> itself. Now I need to apologize for Windsor, it wasn't its fault all along.

Comments

JH
10/23/2007 05:22 PM by
JH

I've found this post to be quite helpful:

http://blogs.msdn.com/dinesh.kulkarni/archive/2005/09/09/463001.aspx

Avish
10/23/2007 07:16 PM by
Avish

I was a little puzzled by it too on the first time, but when I ventured deeper into generics I realized this really is very logical. In fact, it wouldn't make sense any other way.

See, when you say:

public class Foo : IFoo

The first means "I'm declaring a generic parameter, let's call it T". But the second one means "Now I'm USING said generic parameter, in effect saying that any type constructed from Foo will implement IFoo for the same T."

The fact that you used the same name (T) for the generic parameter declaration on IFoo is irrelevant; inside the scope of the generic class, T refers to the already-declared generic parameter.

If IFoo had IFoo<> in its interface map, nothing would work. That's more apparent when you consider stuff like "public class Thingy : IFoo<IEnumerable>". Here it's very obvious that you want the implemented interface to use the unbound generic parameter T, so that when you construct a concrete type out of that generic type, using System.Int32 for T, it'd implement IFoo<IEnumerable>. .

If that's any comfort, you can still do:

Assert.AreEqual(

typeof(IFoo<>), 

typeof(Foo<>).GetInterfaces()[0].GetGenericDefinition());
Ayende Rahien
10/23/2007 07:22 PM by
Ayende Rahien

Avish,

Yes, that was what I ended up doing. The problem is that it took me a long time to get to the point of "Oh, so that is it"

Tim Scott
10/23/2007 08:25 PM by
Tim Scott

Incidentally, another way to make it pass is to specify any generic param:

Assert.AreEqual(typeof(IFoo), typeof(Foo).GetInterfaces()[0]);

or :

Assert.IsTrue(typeof(IFoo).IsAssignableFrom(typeof(Foo)));

Omer van Kloeten
10/23/2007 10:36 PM by
Omer van Kloeten

Should be "public class Foo : IFoo { }".

Comments have been closed on this topic.