What is the practical purpose of contravariance and covariance in C#? [duplicate] - c#

I Have the following piece of code. I have not specified any generic parameters and IN/OUT(variance) for this delegate. If I understand the meaning of invariance correctly i should not be able to return object of Base type since my delegate mentions return type of object.
Is my understanding of invariance wrong?
class Program
{
public delegate object SampleDelegate(Base b);
static void Main(string[] args)
{
List<Base> listBases = new List<Base>(){new Base{}, new Base{}};
SampleDelegate newDel = new SampleDelegate(ProcessBase);
newDel(new Base() { });
Console.ReadLine();
}
public static Base ProcessBase(Base b)
{
return b;
}
public class Base
{
}
public class Derived : Base
{
}
}

If I understand the meaning of invariance correctly i should not be able to return object of Base type since my delegate mentions return type of object. Is my understanding of invariance wrong?
Since you can compile and run that program, you already know the answer to that question. Yes.
Let's ask the question you meant to ask:
Since the delegate is not even generic, clearly generic variance on delegates does not apply. Why then can I make a covariant conversion from a method returning Base to a delegate type that requires that the method return object?
Clearly generic covariance is not the kind of covariance that is relevant; there is an entirely different rule at play here. This conversion was first allowed in C# 2.0. When converting from a method group to a delegate, the method chosen from the method group may have a return type more general than the delegate's return type, provided that both types are reference types. And similarly for the parameter types, which are contravariant.
The feature of allowing conversions between generic delegate types constructed with reference types to similarly be covariant and contravariant was added -- by me, incidentally -- to C# 4.0.

All delegates allow a degree of covariance in that one can assign a method with a more derived return type to a delegate. This is what you did here, where you had a method with return type Base and a delegate of return type object.
Essentially the value returned is cast to object on invocation, which will always work, because the cast from Base to object is always going to work.
Or to look at it another way, to call ProcessBase via a delegate of type SampleDelegate is to call (object)ProcessBase(theArgument).
You can think of this as the opposite to how we can always call a method with arguments of more derived types. E.g. we can do ReferenceEquals("abc", 1) because "abc" can be cast to object as a more derived type, and 1 can be cast to òbject` by boxing.
And indeed, for similar reasons you could assign ProcessBase to a delegate defined as public delegate object SampleDelegate(Derived b); because it would always be safe to call it, because it could only ever be called with a Derived argument, which could always be cast to Base on calling.
More often when we talk about covariance and contravariance with delegates in C#, we mean that of covariant and contravariant type parameters.
(Mainly because the type of variance described above was in the language since 2.0, and the type I'll describe below since 4.0, so the latter type was "news" to already-working C# programmers).
If we have a generic delegate defined as:
public delegate TResult MyDelegate<TArg, TResult>(TArg argument);
Then consider the following:
MyDelegate<Base, Derived> del = b => null;//simple example.
MyDelegate<Derived, Derived> del2 = del; // compiler error 1. CS0029: Cannot implicitly convert type
MyDelegate<Base, Base> del3 = del; // compiler error 2. CS0029: Cannot implicitly convert type
MyDelegate<Derived, Base> del4 = del; // compiler error 3. CS0029: Cannot implicitly convert type
We aren't allowed to do either of the two conversions, but when you think about it, what could possibly go wrong here?
If we change the definition of MyDelegate to:
public delegate TResult MyDelegate<in TArg, TResult>(TArg argument);
Then the first error goes away, because the in allows contravariance on TArg.
If we change the definition to:
public delegate TResult MyDelegate<TArg, out TResult>(TArg argument);
Then the second error goes away, because the out allows covariance on TResult.
Finally if we change the definition to:
public delegate TResult MyDelegate<in TArg, out TResult>(TArg argument);
Then all three errors go away, because we have both the in and the out.
The rules of covariance an contravariance won't allow you to assign anything illogical. E.g.:
public delegate TResult MyDelegate<out TArg, in TResult>(TArg argument);
Has two errors: We cannot expect to be safely covariant on TArg nor safely contravariant on TResult; if we were allowed to do this, we would be allowed to assign delegates that didn't work to other delegate types.
The Func and Action types provide examples of this. For example, one of them is defined as:
public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
And hence we can assign a Func<object, object, string> to a variable of type Func<string, string, object> because all calls involved will work, but we cannot assign a Func<string, string, object> to a variable of type Func<object, object, string>, as this doesn't hold.
Generic covariance and contravariance also holds with interfaces, allowing us to e.g. assign an IEnumerable<string> to a variable of type IEnumerable<object> because everything we can call on an IEnumerable<object> we can safely call on an IEnumerable<string>.

Related

Are there any issues with having a non-generic method in a generic class?

On MSDN (https://learn.microsoft.com/en-us/dotnet/standard/generics/), it says:
Generic methods can appear on generic or nongeneric types. It is important to note that a method is not generic just because it belongs to a generic type, or even because it has formal parameters whose types are the generic parameters of the enclosing type. A method is generic only if it has its own list of type parameters. In the following code, only method G is generic.
Isn't M for all intents and purposes generic? If you call method M on some instance of type T, M can only have input and output parameters of type T.
Would there be any issues with using such a nongeneric method M?
class A
{
T G<T>(T arg)
{
T temp = arg;
//...
return temp;
}
}
class Generic<T>
{
T M(T arg)
{
T temp = arg;
//...
return temp;
}
}
Isn't M for all intents and purposes generic?
If you use reflection to inspect those methods, you'll find that IsGenericMethod and IsGenericMethodDefinition are true for A.G, and false for Generic<>.M. This is true even though Generic<>.M's return type is T, whose IsGenericParameter property is true.
You're asking a question about semantics (i.e. the linguistic meaning of "generic method"), and Microsoft has defined the meaning of these concepts as they pertain to C#. So when Microsoft says that a generic method is one that takes generic type arguments, that's the definition that's going to be accepted, both by developers and by the framework itself.
If you call method M on some instance of type T, M can only have input and output parameters of type T.
Yes, the difference is in the scope of the type T. For example, if you have an instance of Generic<string>, you cannot pass its M an int. With the generic method, the generic arguments can be changed at the method level, whereas a non-generic method on a generic class uses types that match the generic types of the class that it belongs to.
Would there be any issues with using such a nongeneric method M?
There are no "issues" with using a non-generic method like M inside of a generic class. That's how generic classes were meant to work. Just be aware of the differences in behavior I mentioned above.

Is covariance all about accepting values?

Is covariance all about accepting values?
I am learning the C# through the CLR via C# book. And I come across the following excerpt:
Since T is covariant, it is possible to have the following code compile and run successfully:
// This method accepts an IEnumerable of any reference type
Int32 Count(IEnumerable<Object> collection) { ... }
...
// The call below passes an IEnumerable<String> to Count
Int32 c = Count(new[] { "Grant" });
I am confused here. Because the covariance is about having the type which is one of the base types of the required type. And as such the covariance is used only in the context of the return types. While in the example above we have a String (which is derived from Object, so that is contravariant, but not covariant) which is used in the context of passing arguments (but not returning values).
So, should we use in the example above the contravariant instead of the covariant (meaning that there is an error in the book)?
UPDATE
After the comments I got another question. Are the following definitions correct?
Contravariant Meaning that the generic type parameter can change from a class to a class
derived from it. In C#, you indicate contravariant generic type parameters with the in keyword.
Contravariant generic type parameters can appear only in input positions such as a method’s
argument.
Covariant Meaning that the generic type argument can change from a class to one of its base
classes. In C#, you indicate covariant generic type parameters with the out keyword. Covariant
As Josh pointed out the book is correct.
You can check this link if you want to confirm it from another source.
IEnumerable<Cat> is a subtype of IEnumerable<Animal>. The subtyping is preserved because IEnumerable<T> is covariant on T.
The two keywords in C# for those concepts are out for covariant and in for contravariant. In the IEnumerable<T> case this translate to IEnumerable<out T>.
Hope this helps.
UPDATE
You would have to inverse your definitions as follows.
Covariant Meaning that the generic type parameter can be a certain class and all the derived classes from it (IEnumerable<Object> can be a IEnumerable<String> since String is a subtype of Object). In C#, you indicate covariant generic type parameters with the out keyword.
Contravariant Meaning that the generic type argument can change from a
class to one of its base classes. In C#, you indicate contravariant
generic type parameters with the in keyword.

C# -- Knowing a base type of a Generic Type

How can I find out the base type of a generic type?
For example
Func<A, B>
I'd like to be able to say this is a Func<> .. but apparently, Func<,> is different from Func<> -- Is there a way to somehow catch them both, or Func<,,,> etc?
You're looking for GetGenericTypeDefinition:
var t = typeof(Func<int, string>);
var tGeneric = t.GetGenericTypeDefinition();
Assert.AreEqual(typeof(Func<,>), tGeneric);
If you then want to know if a type is one of the many Func<> variants, then your best best is simply to do something like this. Checking type names, as suggested elsewhere is absolutely NOT the way to check type identity:
static Type[] funcGenerics = new[]{
typeof(Func<>), typeof(Func<,>), typeof(Func<,,>), typeof(Func<,,,>),
/* and so on... */
}
//there are other ways to do the above - but this is the most explicit.
static bool IsFuncType(Type t)
{
if(t.IsGenericTypeDefinition)
return funcGenerics.Any(tt => tt == t);
else if(t.IsGenericType)
return IsFuncType(t.GetGenericTypeDefinition());
return false;
}
Your terminology is incorrect - which I suspect why you got a downvote on your question. A base type is one that a type inherits from (not an interface, which is different, although conceptually very similar).
A generic type definition is best thought of as being like a template (the strong qualification there because the term 'template' is used in C++ and, while visually similar they are very different in implementation).
More accurately, Func<,> is a generic type definition whereas Func<int, string> is a closed generic (the 'generic type').
You can also have an open generic, which is where the type arguments are generic parameters - e.g, given:
class MyType<T> : List<T> { }
Then List<T> is an open generic with the generic type definition List<>, because T is a generic parameter which will not be closed till MyType<T> is referenced with a concrete type argument, such as int or string.
Finally, just because a bunch of generic types share the same common name, e.g. Func<>, Func<,>, and Func<,,> it does not mean they are in any way related. At the type level, there is no explicit connection, which is why you have to check for all these type identities, and why there is no common 'base' as you put it. If they all had a common interface or base class, however, then you could - by checking for compatibility with that interface or base type.
Given a generic type definition, you can construct generic types using MakeGenericType, as has been mentioned by Jeffrey Zhang.
No, you can't, There is not a base type of a Gerneric Type. If you want to get a specific generic type by type parameters, You can use MakeGenericType. For example:
//get Func<int, string> type
typeof(Func<>).MakeGenericType(typeof(int), typeof(string));
If you want to get a Generic Type from specified generic type, You can use GetGenericTypeDefinition. For example:
//get Func<,> type
typeof(Func<int, string>).GetGenericTypeDefinition();
It is Because Func< A, B > does not inherit from Func<> It is a generic based on Func<,>.
However, you will notice that
typeof(Func<int, int>).FullName // equals "System.Func`2...
typeof(Func<int, int, int>).FullName // equals "System.Func`3...
It is a bit ugly but you could use something like
YourType.FullName.StartsWith("System.Func")
Hope it helps
Edit:
Why not use YourType.GetGenericTypeDefinition()?
Because typeof(Func<int, int>).GetGenericTypeDefinition() returns Func<,>
and typeof(Func<int, int, int>).GetGenericTypeDefinition() return Func<,,>.
Func<,> and Func<,,> are not the same Type.

Is it possible to create a delegate for *any* Action<T>? [duplicate]

This question already has answers here:
Why covariance and contravariance do not support value type
(4 answers)
Closed 8 years ago.
I'm trying to figure out why this doesn't work:
public void DefaultAction( object obj = null ){}
public void Start()
{
SomeReferenceType obj;
DefaultAction( obj ); //works
int i;
string s;
DefaultAction( i ); //works
DefaultAction( s ); //works
}
//however...
public event Action OnNullAction = DefaultAction; //works
public event Action<SomeReferenceType> OnObjectAction = DefaultAction; //works
public event Action<int> OnIntAction = DefaultAction; //doesn't work!!
Trying to bind a void(object) to an Action<ValueType> throws a parameter mismatch error, even though you can call the function directly with an int/string/bool what have you. Is there some mysterious boxing/unboxing happening? And regardless, is it possible to create a delegate that can respond to any Action<T>?
You have discovered that delegate contravariance requires reference types.
That's pretty highfalutin, I know.
First off, let me clearly state what covariance and contravariance are. Suppose you have a relationship between types: "A value of type Giraffe can be assigned to a variable of type Animal". Let's notate that as
Animal <-- Giraffe
A generic type C<T> is said to be covariant in T if replacing every type with C<that type> preserves the direction of the arrow.
IEnumerable<Animal> <-- IEnumerable<Giraffe>
Since C# 4.0, when I added this feature to the language, ou can use a sequence of giraffes anywhere you need a sequence of animals.
A generic type C<T> is said to be contravariant in T if the replacement reverses the direction of the arrow:
Action<Animal> --> Action<Giraffe>
If you need an action that requires that you give it a Giraffe and you have an action that can take any Animal, then you're all set; you need something that can take a Giraffe and an Action<Animal> can take a Giraffe. But this is not covariant. If you have an Action<Giraffe> in hand and you need an Action<Animal>, you can't use the Action<Giraffe> because *you can pass a Tiger to an Action<Animal> but not an Action<Giraffe>.
What about Func<T>? It is covariant in T. If you need a function that returns an Animal and you have a function that returns a Giraffe, you're good, because the Giraffe will be an Animal.
What about Func<A, R>? It is contravariant in A and covariant in R. It should be clear why.
So now that we know what covariance and contravariance of generic types are, what are the rules in C#? The rules are:
The type declarations must be annotated with in (contravariant) and out (covariant). For example, delegate R Func<in A, out R>(A a). Notice that the in is the thing that goes into the function and the out is the thing that comes out of the function; we named them in and out on purpose.
The compiler has to be able to prove that the annotations are safe. See the spec or my blog for details.
Variance is only supported on generic delegates and interfaces, not on generic structs, enums or classes.
The varying types have to both be reference types.
So now we come to your question. Why do they have to both be reference types? You deduced the answer: where is the boxing instruction?
Action<object> oa = (object x)=>whatever;
Action<int> ia = oa; // Suppose this works.
ia(123);
Where is the boxing instruction? Not in the body of the lambda assigned to oa -- that thing takes an object already. Not in the call to ia(123) -- that thing takes an integer. The only possible solution is that oa and ia be unequal; that this be a shorthand for
Action<object> oa = (object x)=>{whatever};
Action<int> ia = (int x)=>{ oa(x); };
But if that's what you meant to say, then just say that. People expect that a reference conversion will maintain referential identity, so C# outlaws covariant or contravariant conversions that would have to box or unbox a value.
If you have more questions on this, search my old blog (blogs.msdn.com/ericlippert) for covariance or search for the C# covariance FAQ.
Just make it generic
public void DefaultAction<T>(T param) { }

Can someone explain this generic syntax?

I've done a bit of google on this and have not found anything to explain the syntax..
So I'm using Action<T> for a producer consumer where Action<T> is the consumer action to perform. I right clicked on Action<T> - 'go to definition' to see its definition and this is what I was shown
public delegate void Action<in T>(T obj);
Hmmm... I've been using generics for quite a while now and have not ever used nor seen the syntax of <in T>
Anyone got a nice summation to hand to explain the syntax and what it produces?
Thanks. D
The in and out variance modifiers for generic types has been introduced in C# 4.0 to allow co and contra variance to generic types.
For generic type parameters, the in keyword specifies that the type parameter is contravariant.
For generic type parameters, the out keyword specifies that the type parameter is covariant.
And from Variance in Generic Interfaces (C# and Visual Basic) on MSDN:
Covariance permits a method to have a more derived return type than that defined by the generic type parameter of the interface.
Contravariance permits a method to have argument types that are less derived than that specified by the generic parameter of the interface.

Categories

Resources