Is covariance all about accepting values? - c#

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.

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.

INamedTypeSymbol inheritance relation in the Roslyn

I have created INamedTypeSymbol for concrete types using GetTypeByMetadataName
INamedTypeSymbol listTypeSymbol = context.SemanticModel.Compilation.GetTypeByMetadataName("System.Collections.Generic.List`1");
INamedTypeSymbol collectionTypeSymbol = context.SemanticModel.Compilation.GetTypeByMetadataName("System.Collections.Generic.ICollection`1");
One of them is a symbol of a runtime type of List<T>, other of them is a symbol of a runtime type of ICollection<T>. The List<T> type is derived from the type of ICollection<T>.
How can I check an inheritance relation for INamedTypeSymbol?
I try to do it using a ClassifyConversion method
Conversion con = context.SemanticModel.Compilation.ClassifyConversion(listTypeSymbol, collectionTypeSymbol);
However this function returns a Explicit Reference value of conversion. I am expected to see something like the Implicit Reference value.
This is a complicated one.
GetTypeByMetadataName("System.Collections.Generic.List`1") returns an open generic type, which is in effect a type constructor that can be used to create closed generic types with specific parameters.
It doesn't make sense to speak about conversions between open generic types; two types A<> and B<> might be differently convertible if B<> is declared as B<T> : A<List<T>> or more-complex relationships.
You can only check for convertibility between closed generic types that share the same type parameter. To do that, call Construct() and pass types for the type parameters.

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.

How is out keyword associated to type co-variance?

I was reading the Covariance and Contravariance in C#.
According to my understanding,
If we have a class Animal and a derived class Cat then,
Covariance feature makes the compiler accepts passing a Cat type object to Animal type object and Contravariant is vice-versa.
Everything is ok till I read this line.
"To annotate type-covariance you use out parameter and for contravariant you use in parameter"
According to me, a parameter decorated with out keyword => the value must be assigned to that particular parameter in a function.
I have difficulties to associate the out keyword to covariance and in keyword to contravariant.
How are these two related ? Any super simple examples for both?
Thanks in advance.
Both covariance and contravariance in C# 4.0 refer to the ability of using a derived class instead of base class. The in/out keywords are compiler hints to indicate whether or not the type parameters will be used for input and output.
Covariance
Covariance in C# 4.0 is aided by out keyword and it means that a generic type using a derived class of the out type parameter is OK. Hence
IEnumerable<Fruit> fruit = new List<Apple>();
Since Apple is a Fruit, List can be safely used as IEnumerable
Contravariance
Contravariance is the in keyword and it denotes input types, usually in delegates. The principle is the same, it means that the delegate can accept more derived class.
public delegate void Func<in T>(T param);
This means that if we have a Func, it can be converted to Func.
Func<Fruit> fruitFunc = (fruit)=>{};
Func<Apple> appleFunc = fruitFunc;
Why are they called co/contravariance if they are basically the same thing?
Because even though the principle is the same, safe casting from derived to base, when used on the input types, we can safely cast a less derived type (Func) to a more derived type (Func), which makes sense, since any function that takes Fruit, can also take Apple.
out is a contextual keyword, i.e. depending on its placement, it means different things.
You are talking about the parameter modifier. But that's not what it is with regards to Co- and Contravariance. When the keyword is used in a generic interface definition, it is the generic modifier, which is something else completely.
out keyword serves different purposes. As you noted in the context of "generic type parameters" it acts as a keyword to make "covariance" work, another use is similar to ref keyword which we use to get multiple return values from a Method.
If you look at the documentation for out-keyword in MSDN two purposes of out keyword will be listed. Not to be confused it acts different based on the context.
You can find similarities with new keyoword also, it also serves different purposes. AFAIK they are
To create new instance of a class. object obj = new object();
To explicitly shadow base class method or property. protected new void BaseMethod(){}
Generic parameter constraint. private void MyGenericMethod<T>(T t) where T:new()
I think Microsoft guys make this feature very complexe by using not clear names for the feature name and not good keywords for the feature parameters.
I know this name from Covariance and contravariance of vectors (Mathe) and when I hear about it first time in C# I got a shock!
#JoesphAlbahari in his book C# 4 In a Nutshell have explained this topic very well page 109-112. I recommend you really to read it.

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