Question about C# 4.0's generics covariance

前端 未结 1 735
抹茶落季
抹茶落季 2021-02-19 16:05

Having defined this interface:

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

Why does the fol

1条回答
  •  予麋鹿
    予麋鹿 (楼主)
    2021-02-19 16:37

    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 when passed an IInputBoxService - 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
    {
        public T Value { get; private set; }
        public Wrapper(T value)
        {
            Value = value;
        }
    }
    

    It's pretty nasty though :(

    0 讨论(0)
    提交回复
    热议问题