views:

146

answers:

1

Having defined this interface:

public interface IInputBoxService<out T> {
    bool ShowDialog();
    T Result { get; }
}

Why does the following code work:

public class StringInputBoxService : IInputBoxService<string> {
    ...
}

...

IInputBoxService<object> service = new StringInputBoxService();

and this doesn't?:

public class IntegerInputBoxService : IInputBoxService<int> {
    ...
}

...

IInputBoxService<object> service = new IntegerInputBoxService();

Does it have anything to do with int being a value type? If yes, how can I circumvent this situation?

Thanks

+13  A: 

Yes, it absolutely has to do with int being a value type. Generic variance in C# 4 only works with reference types. This is primarily because references always have the same representation: a reference is just a reference, so the CLR can use the same bits for something it knows is a string reference as for an object reference. The CLR can make sure that the code will be safe, and use native code which only knows about IInputBoxService<object> when passed an IInputBoxService<string> - the value returned from Result will be representationally compatible (if such a term exists!).

With int => object there would have to be boxing etc, so you don't end up with the same code - that basically messes up variance.

EDIT: The C# 4.0 spec says this in section 13.1.3.2:

The purpose of variance annotations is to provide for more lenient (but still type safe) conversions to interface and delegate types. To this end the definitions of implicit (§6.1) and explicit conversions (§6.2) make use of the notion of variance-convertibility, which is defined as follows: A type T is variance-convertible to a type T if T is either an interface or a delegate type declared with the variant type parameters T, and for each variant type parameter Xi one of the following holds:

  • Xi is covariant and an implicit reference or identity conversion exists from Ai to Bi

  • Xi is contravariant and an implicit reference or identity conversion exists from Bi to Ai

  • Xi is invariant and an identity conversion exists from Ai to Bi

This doesn't make it terribly obvious, but basically reference conversions only exist between reference types, which leaves only identity conversions (i.e. from a type to itself).

As for workarounds: I think you'd have to create your own wrapper class, basically. This can be as simple as:

public class Wrapper<T>
{
    public T Value { get; private set; }
    public Wrapper(T value)
    {
        Value = value;
    }
}

It's pretty nasty though :(

Jon Skeet
I was thinking that was the case, but I can't find a reference. Do you know of any msdn link mentioning that variances only work with reference types?
Cameron MacFarland
How about this one? http://blogs.msdn.com/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx - Use Ctrl+F to find "A couple of important rules to remember" and you'll find the entry.
Lasse V. Karlsen
Is there any alternative? Java defines for all primitive types reference types too, so instead of doing something<int> I can make it something<Integer>, which would solve this problem.
devoured elysium