Variance when creating interfaces - c#

public interface SomeInterfaceName<out T> where T : struct
{
T? SomePropertyName { get; }
}
The error I get is:
error CS1961: Invalid variance: The type parameter 'T' must be invariantly valid on 'SomeInterfaceName.SomePropertyName'. 'T' is covariant.
I do not need this to be Covariant. I could as well delete the out keyword. But then Resharper is suggesting I could use Covariance and I agree, I don't see why I could not. I'm only using T as return value. Or is it because Nullable<> does not support it?
Can anybody explain the error?

It's pointless to make a type parameter with a struct constraint covariant. Generic variance isn't supported for value type type arguments at all - so for example, there's no conversion from IEnumerable<int> to either IEnumerable<long> or IEnumerable<object> even though there are conversions from int to both long and object.
The problem you're actually running into is that Nullable<T> isn't covariant, but you're trying to use T in a property of type Nullable<T>. That's what's causing the error.
Personally I think it would be better if type parameters with struct constraints couldn't be declared to be covariant or contravariant (given that it won't be useful) but that in itself isn't prohibited.

Related

Implicit convertion of generic types parameterized with reference types vs value types

Why does C# implicitly convert generic types parameterized with a reference type implementing an interface to the same generic type parameterized with the implemented interface, but not perform the same implicit conversion for reference types?
Essentially, why does the first line compile but the second one fail?
IEnumerable<IComparable<Version>> x = Enumerable.Empty<Version>();
IEnumerable<IComparable<int>> y = Enumerable.Empty<int>();
Especially great would be a reference to the part of the spec that describes this behavior.
Short answer
Despite the name "implicit", implicit conversions don't apply unless the rules explicitly say they do, and the rules don't allow boxing conversions when going from IEnumerable<int> to IEnumerable<IComparable<int>>. As a simpler case, you can't go from IEnumerable<int> to IEnumerable<object> for the same reason, and that case is well documented.
Long answer
OK, first of all, why would IEnumerable<T> convert to IEnumerable<IComparable<T>> at all? This is covered in §6.1.6 (C# Language Specification 5.0):
The implicit reference conversions are:
[...]
From any reference-type to an interface or delegate type T if it has an implicit identity or reference conversion to an interface or
delegate type T0 and
T0 is variance-convertible (§13.1.3.2) to T.
And §13.1.3.2 says:
A type T<A1, …, An> is
variance-convertible to a type T<B1, …, Bn>
if T is either an interface or a delegate type
declared with the variant type parameters T<X1, …, Xn>,
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
Since IEnumerable<T> is covariant in T, this means that if there is an implicit reference or identity conversion from T to IComparable<T>, then there is an implicit reference conversion from IEnumerable<T> to IEnumerable<IComparable<T>>, by virtue of these being variance-convertible.
I emphasized "reference" for a reason, of course. Since Version implements IComparable<Version>, there is an implicit reference conversion:
From any class-type S to any interface-type T, provided S
implements T.
Right, so now, why doesn't IEnumerable<int> implicitly convert to IEnumerable<IComparable<int>>? After all, int implicitly converts to IComparable<int>:
IComparable<int> x = 0; // sure
But it does so not through a reference conversion or an identity conversion, but through a boxing conversion (§6.1.7):
A boxing conversion exists from any non-nullable-value-type [...] to
any interface-type implemented by the non-nullable-value-type.
The rules of §13.1.3.2 do not allow boxing conversions in considering whether a variance conversion is possible, and there is no other rule that would enable an implicit conversion from IEnumerable<int> to IEnumerable<IComparable<int>>. Despite the name, implicit conversions are covered by explicit rules.
There is actually a much simpler illustration of this problem:
object x = 0; // sure, an int is an object
IEnumerable<object> x = new int[] { 0 }; // except when it's not
This isn't allowed for the same reason: there is no reference conversion from int to object, only a boxing conversion, and those are not considered. And in this form, there are several questions on Stack Overflow that explain why this is not allowed (like this one). To summarize: it's not that this is impossible, but to support it, the compiler would have to generate supporting code to stick the code for the boxing conversion somewhere. The C# team valued transparency in this case over ease of use and decided to allow identity-preserving conversions only.
Finally, as a matter of practical consideration, suppose you had an IEnumerable<int> and you needed an IEnumerable<IComparable<int>>, how would you get it? Well, by doing the boxing yourself:
Func<int, IComparable<int>> asComparable = i => i; // compiles to ldarg ; box ; ret
IEnumerable<IComparable<int>> x = Enumerable.Empty<int>().Select(asComparable);
Of course using Enumerable.Cast would be more practical here; I wrote it this way to highlight that an implicit conversion is involved. There is a cost to this operation, and that's just the point; the C# designers wanted this cost to be explicit.

Variance rules in C#

The Exact rules for variance validity are a bit vague and not specific. I'm going to list the rules for what makes a type valid-covariantly, and attach some queries and personal annotations to each of those rules.
A type is valid covariantly if it is:
1) a pointer type, or a non-generic type.
Pointers and non-generic types are not variant in C#, except for arrays and non-generic delegates. Generic classes, structs and enums are invariant. Am I right here?
2) An array type T[] where T is valid covariantly.
So this means that if the element type T of an array T[] is covariant (reference or array element type), then the array is covariant, and if the element type is invariant (value type), then the Array type is invariant. Arrays cannot be contravariant in C#. Am I right here?
3) A generic type parameter type, if it was not declared as being contravariant.
We normally say that a generic type is variant on a parameter type, but for a parameter type to be variant on it's own. Is this another short form for saying that? for example, the generic type T<out D> is covariant on D (hence covariantly valid), hence we can say that the type parameter D is covariantly valid. Am I right?
4) A constructed class, struct, enum, interface or delegate type X might be valid covariantly. To determine if it is, we examine each type argument differently, depending on whether the corresponding type parameter was declared as covariant (out), contravariant (in), or invariant (neither). (Of course the generic type parameters of classes and structs will never be declared 'out' or 'in'; they will always be invariant.) If the ith type parameter was declared as covariant, then Ti must be valid covariantly. If it was declared as contravariant, then Ti must be valid contravariantly. If it was declared as invariant, then Ti must be valid invariantly.
This last rule, from top to bottom, is utterly ambiguous.
Are we talking about a generic type's variance on all of its in/out/invariant type parameters? By definition, A generic type can be covariant/contravariant/invariant on one type paramter at a time. To be covariant or invariant, in this case, on all of it's type parameters at once doesn't hold any meaning. What could that mean?
Moving forward. To determine if the generic type is covariantly valid, we examine its type arguments (not type paramters). So if the corresponding type parameter is covariant/contravariant/invariant, then the type argument is valid covariantly/contravariantly/invariantly respectively ...
I need this rule be explained in more depth.
Edit: Thanks Eric. Greatly appreciated!
I do perfectly understand what valid covariantly/contravariantly/invariantly mean. A type is valid covriantly, if it's definitely not contravariant, which means that it can be invariant. perfectly fine!
For the 4th rule, you follow the procedure of how to determine whether a constructed generic type is valid covariantly, as defined in the rule. But, how do you determine if a type argument that's declared as covariant (out) is covariantly valid?
For example, in the closed constructed interface I { } of the generic interface I { ... }, shouldn't the very fact that the type argument object is declared as a covariant type parameter(out U) in the generic interface declaration mean that the type argument object is covariant? I think it should. Cuz that's the very definition of being covariant.
Also, the second rule:
2) An array type T[] where T is valid covariantly.
What does the array element type T being valid covariantly mean? Do you mean the element type being a value type (invariant in this case) or a reference type (covariant in this case)?
Cuz the projection T → T[] is only variant if T is reference type.
You are right that the last rule is the hardest one to understand but I assure you it is not ambiguous.
An example or two will help. Consider this type declaration:
interface I<in T, out U, V> { ... }
Is this type covariantly valid?
I<string, object, int> { }
Let's go through our definition.
To determine if it is, we examine each type argument differently, depending on whether the corresponding type parameter was declared as covariant (out), contravariant (in), or invariant (neither).
OK, so the type arguments are string, object and int. The corresponding parameters are in T, out U and V, respectively.
If the ith type parameter was declared as covariant (out), then Ti must be valid covariantly.
The second type parameter is out U, so object must be valid covariantly. It is.
If it was declared as contravariant (in), then Ti must be valid contravariantly.
The first was declared in T, so string must be valid contravariantly. It is.
If it was declared as invariant, then Ti must be valid invariantly.
The third V was invariant, so int must be valid invariantly; it must be both valid contravariantly and covariantly. It is.
We pass all three checks; the type I<string, object, int> is valid covariantly.
OK, that one was easy.
Now let's look at a harder one.
interface IEnumerable<out W> { ... }
interface I<in T, out U, V>
{
IEnumerable<T> M();
}
IEnumerable<T> inside I is a type. Is IEnumerable<T> as used inside I valid covariantly?
Let's go through our definition. We have type argument T corresponding to type parameter out W. Note that T is a type parameter of I and a type argument of IEnumerable.
If the ith type parameter (W) was declared as covariant (out), then Ti (T) must be valid covariantly.
OK, so for IEnumerable<T> in I to be valid covariantly, T must be valid covariantly. Is it? NO. T was declared as in T. A type parameter that is declared in is never valid covariantly. Therefore the type IEnumerable<T> as used inside I is not valid covariantly, because the "must" condition is violated.
Again, like I said in my answer to your previous question, if "valid covariantly" and "valid contravariantly" are giving you grief, just give them different names. They are well-defined formal properties; you can call them anything you want if it makes it easier for you to understand.
No, your annotations are messed up.
That article is really hard to understand, in part because "valid covariantly" has nothing at all to do with covariance. Eric does point that out, but it means for every sentence you have to "unthink" the natural meaning, then think in terms of these weird definitions for "valid covariantly", "valid contravariantly", and "valid invariantly".
I strongly recommend you instead read about the Liskov Substitution Principle and then think about substitutability. Covariance, contravariance, and invariance have very simple definitions when looked at from the LSP perspective.
Then, you may notice that the C# rules at compile-time don't exactly match up with LSP (unfortunately -- and this is mainly a mistake made in Java and copied into C# to help court Java programmers). On the other hand, at runtime the LSP rules have to be followed, so if you start with those, you'll write code that both compiles and runs correctly, which I think is a more worthwhile endeavor than learning the C# language rules (unless you're writing a C# compiler).
how do you determine if a type argument that's declared as covariant (out) is covariantly valid?
Read rule 3.
in the closed constructed interface I{string, object int> of the generic interface I<in T, out U, V>, shouldn't the very fact that the type argument object is declared as a covariant type parameter out U in the generic interface declaration mean that the type argument object is covariant?
First, you're using "covariant" where you mean "covariantly valid". Remember, these are different things.
Second, let's go through it again. Is object covariantly valid? Yes, by rule 1. Is I<string, object, int> covariantly valid? Yes, by rule 3, which states that:
The type argument corresponding to T must be contravariantly valid.
The type argument corresponding to U must be covariantly valid.
The type argument corresponding to V must be both.
Since all three conditions are met, I<string, object, int> is covariantly valid.
In "An array type T[] where T is valid covariantly" what does the array element type T being valid covariantly mean?
I don't understand the question. We're defining what "covariantly valid" means. Rule 2 is part of the definition of "covariantly valid".
As an example, is object[] covariantly valid? Yes, because object is covariantly valid. If we had:
interface IFoo<out T> { T[] M(); }
Is T[] covariantly valid? Yes, because T is covariantly valid.
If we had
interface IBar<in T> { T[] M(); }
Is T[] covariantly valid? No. For an array type to be covariantly valid its element type must be covariantly valid, but T is not.

What does "out" mean before a Generic type parameter?

I've just saw an unfamiliar syntax while looking for GroupBy return type:
public interface IGrouping<out TKey, out TElement> : IEnumerable<TElement>
MSDN Source
I know what does out mean in methods, but not in a generics interface.
What does out mean in a generic type?
It denotes a covariant parameter. See also the description on MSDN. Essentially it says, that IGrouping<Aderived, Bderived> can be regarded as IGrouping<Abase, Bbase>, hence you can
IGrouping<Aderived, Bderived> gr = MakeGrouping(...);
IGrouping<Abase, Bbase> grBase = gr;
if Aderived is an interface or a type derived from Abase. This is a feature that comes in handy when you want to call a method that requires a parameter of type IGrouping<Abase, Bbase>, but you only got an object of type IGrouping<Aderived, Bderived>. In this case, both types can be considered equivalent due to the covariance of their type parameters.
It is one of the two generic modifiers introduces in C# 4.0 (Visual Studio 2010).
It signifies that the generic parameter it is declared on is covariant.
The in modifier signifies the generic parameter it is declared on is contravariant.
See out (Generic Modifier) and in (Generic Modifier) on MSDN.
out just means that the type is only used for output e.g.
public interface Foo<out T>
{
T Bar()
}
There also is a modifier in that means that the type is only used for input e.g.
public interface Foo<in T>
{
int Bar(T x)
}
These are used because Interfaces with in are covariant in T and Interfaces with out are contravariant in T.
out keyword in this context would indicate the corresponding type parameter to be covariant simply speaking - covariance enables you to use a more derived type than that specified by the generic parameter.
BTW, see this ten part series from Eric Lippert to understand more about covariance and contra-variance: http://blogs.msdn.com/b/ericlippert/archive/2007/10/16/covariance-and-contravariance-in-c-part-one.aspx

Tuple.Create return type

Given
void foo(Tuple<object> t)
{
}
void bar()
{
foo(Tuple.Create("hello"));
}
the c# compiler returns
error CS1502: The best overloaded method match for 'foo(System.Tuple<object>)' has some invalid arguments
error CS1503: Argument 1: cannot convert from 'System.Tuple<string>' to 'System.Tuple<object>'
Adding explicit types to Tuple.Create defeats its purpose. How can I convince the compiler to accept the code?
FWIW, I think C++ doesn't have this problem: http://live.boost.org/doc/libs/1_33_1/libs/tuple/doc/tuple_users_guide.html#constructing_tuples
This is the same generic type covariance issue that comes up daily. It is simply not possible to convert Foo<T> to Foo<SubT> or vice-versa. Starting with .NET 4, it is supported - but only for interfaces and delegates, and by explicitly specifying the generic type parameter as variant by declaring it Foo<out T1>.
You can make the code compile by not using Tuple<object> but using Tuple<T>
void foo<T>(Tuple<T> t)
If you don't want to do that, you simply will need to be explicit with the string to object in the Tuple.Create method.
Tuple.Create<object>("Hello");
Tuple.Create((object)"Hello");
Consider if you could have Tuple<object> and then pass in a Tuple<string>. What if your signature was
void(ref Tuple<object> t)
There's nothing that stops you from writing in that method
t = new Tuple<object>(1);
And now you've just put a 1 in a tuple that only allows strings. Granted, it's a corner case as Tuple is inherently readonly so you need a ref parameter, but it's a problem case, nonetheless.
You are trying to turn a Tuple<string> into a Tuple<object>, which you cannot do - generic variance is only supported for interfaces and delegate. You need to explicitly specify the type arguments to Tuple.Create:
void bar()
{
foo(Tuple.Create<object>("hello"));
}

How to implement == or >= operators for generic type

I have a generic type Foo which has a internal generic class Boo. Boo class a property Value of type K. In a method inside Foo i want to do a boo.Value >= value Note that second operand value is of type T. while compiling i am getting following error:
Operator '>=' cannot be applied to operands of type 'T' and 'T'
Can anyone please tell me whats the problem here?
Restrict the type argument to IComparable , then you can implement the operations with the CompareTo method. Of course, you won't be able to use your generic class with every type, but I think every built-in type which can be compared using such operators like >, <=, ... implements this interface.
Best Regards,
Oliver Hanappi

Categories