Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Question about C# 4.0's generics covariance

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

like image 907
devoured elysium Avatar asked Apr 28 '10 06:04

devoured elysium


People also ask

Why is C very important?

C is called as a robust language, which has so many built-in functions and operations, which can be used to write any complex program. Generally, we use to call C as a middle level language. Because, the 'C' compiler combines the capabilities of an assembly language with the features of a high-level language.

What is the main point of C?

One of the most significant features of C language is its support for dynamic memory management (DMA). It means that you can utilize and manage the size of the data structure in C during runtime. C also provides several predefined functions to work with memory allocation.

What is unique about C?

No Explicit String Type. Unlike most other modern languages such as Java, C++ and JavaScript, C does not provide a separate type for strings. A string is considered an array of characters terminated by a 0 character (denoted "\0").


1 Answers

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 :(

like image 66
Jon Skeet Avatar answered Sep 30 '22 10:09

Jon Skeet