0

I know I'm missing something, but shouldn't this work!?

public interface IFoo<out TA, in TB>
    where TA : class
    where TB : class
{
}
public class Foo<T> : IFoo<T, T> where T : class { }
public class Whatever { }

...

Foo<Whatever> _Foo = new Foo<Whatever>(); // (changed "Foo" to "_Foo to be more clear)
var f = (IFoo<object, object>)_Foo; // CAST ERROR

FYI: The original project is .NET 4.0 using VS 2013.

Edit: It appears that an 'in' type for the 'TB' parameter (contravariance) must be either the SAME type, or a DERIVED type. Since 'object' is a supertype (as in, 'object' does not derive from type 'Whatever'), the conversion fails. (thanks to Aasmund Eldhuset)

This does work:

public class WhateverB : Whatever { }
var f = (IFoo<object, WhateverB>)Foo; // YAY ;)
James Wilkins
  • 6,836
  • 3
  • 48
  • 73
  • Your code does not compile. Do you have a variable named `Foo`? If so what is its type? – D Stanley Mar 31 '15 at 22:00
  • But an interface cannot be both covariant and contravariant on the same type. What are you trying to achieve? – D Stanley Mar 31 '15 at 22:02
  • `var f = (IFoo)Foo;` here, `Foo` appears to be used as if it were a variable or property, can you show how it is declared? – AaronLS Mar 31 '15 at 22:11
  • The only way this cast can work is if `Foo` is an instance of `Foo`. If you have the same type parameter in a covariant and contravariant position then it is invariant. – Lee Mar 31 '15 at 22:12
  • Sorry, I added a missing line. This code does compile, but errors out at runtime. – James Wilkins Mar 31 '15 at 22:27
  • @DStanley: You may be able to pull off a covariant and contravariant parameter of the same type using this kind of trick, but it would be equivalent to a normal generic (as nothing can be both a super and sub class of a class but that class itself). – Guvante Mar 31 '15 at 22:35

1 Answers1

3

A Foo<string> is not an IFoo<object, object>, because the generic type parameter TB doesn't match - for an in generic type parameter, the actual type must be equal to or be a superclass of the generic type parameter, but string is not a superclass of object.

The reason the cast is accepted by the compiler is that a cast to an interface type is almost always legal (except in the corner case that PetSerAl mentions), because it could be that some subclass of the type you're casting from implements the interface.

Aasmund Eldhuset
  • 37,289
  • 4
  • 68
  • 81
  • `cast to an interface type is always legal` How about cast from sealed class to not implemented interface? – user4003407 Mar 31 '15 at 22:35
  • Sorry, I put in string by mistake thinking it acted like a class. In fact, it doesn't matter, as using a proper class still doesn't work. Take another look. – James Wilkins Mar 31 '15 at 22:36
  • So you are saying that Foo does not equal IFoo? That doesn't even make sense. IFoo should equal IFoo. – James Wilkins Mar 31 '15 at 22:38
  • @JamesWilkins - `IFoo` is not convertible to an `IFoo` since `TB` is contravariant. – Lee Mar 31 '15 at 22:46
  • Oh, nevermind, I see what you mean. I didn't read carefully enough. The "in" type must be a SUPERclass, got it. I read the MSDN article, but as usual, it's useless (info missing). – James Wilkins Mar 31 '15 at 22:53
  • Making TB type 'Whatever' in the conversion makes it work, so yes, you're right. ;) Thanks. – James Wilkins Mar 31 '15 at 22:54