Is it possible for C# generic to be covariant and contravariant? - c#

I have read some content on variance, and want to make sure I fully get it. My understanding is that covariant is implicit conversion of the more-derived type to less and contravariance is the opposite. I further believe that covariance corresponds to the out keyword of the generic type definition. For example IEnumerable interface definition uses out to export the result of an argument, while IComperable's interface definition uses the in keyword to receive a value. I have not seen a generic type which does both, and don't see why it may not be possible. What am I missing?
I also read about the array[] support of variance and how there is a runtime check to make sure the types are the same.

Related

When does type checking of generic definitions and instantiations happen in C#?

In C#,
does type checking of generic definitions happen at compile time?
does type checking of instantiations of generics happen at run time?
Thanks.
The above questions are for me to understand the quotes in bold from C# in a Nutshell:
However, with C# generics, producer types (i.e., open types such as
List ) can be compiled into a library (such as mscorlib.dll).
This works because the synthesis between the producer and the
consumer that produces closed types doesn’t actually happen until
runtime.
To dig deeper into why this is the case, consider the Max method in
C#, once more:
static T Max <T> (T a, T b) where T : IComparable<T>
=> a.CompareTo (b) > 0 ? a : b;
Why couldn’t we have implemented it like this?
static T Max <T> (T a, T b)
=> (a > b ? a : b); // Compile error
The reason is that Max needs to be compiled once and work for all
possible values of T . Compilation cannot succeed, because there is
no single meaning for > across all values of T —in fact, not
every T even has a > operator.
I also have the same question for Java.
Both generic definitions and instantiations are checked at compile time. Further, they can be checked separately. Unlike C++ where you may have an error in your template that you don't discover until you later try to instantiate it, in C#, any compile errors in a generic declaration will be found when the declaration itself is compiled.
The magic that enables this that C++ lacks is constraints. This is what the example is showing you.
When you define a generic method or class, you can put constraints on the type parameters. Those limit which instantiations are allowed but also determine what operations you can take advantage of in the body of the generic declaration.
When the declaration is compiled, the compiler checks that you don't do anything with a type parameter that its constraints don't allow. So, for example, you'd get an error here:
T Foo<T> (T a) => a.CompareTo(b);
You're trying to call CompareTo on a, whose type is T. The compiler has no way of knowing that a user will only instantiate Foo with types that do have that method, so it pessimistically assumes it could be instantiated with a type that doesn't have that and prevents you from compiling this declaration.
When you change it to:
T Foo<T> (T a) where T : IComparable => a.CompareTo(b);
Now it knows every instantiation of T must have a CompareTo() method, so it compiles this.
Later when someone tries to instantiate Foo with some type, if the type does not implement IComparable, they get a compile error. Since the method says "You can only use me with types that implement IComparable", the compiler ensures they meet that constraint.
The main purpose of type-checking is to detect errors at compile-time i.e. before the software is used. This prevents bugs from reaching the users.
C# generic types are checked at compile-time. See the Benefits of Generics.
C# also does type-checking at run-time in certain situations, but that is too late to prevent bugs - the application is already running and being used.
Well, the compiler checks the usage of generic argument at compile time and generate “generic aware type”. There rea especial instructions in IL for this kind of operation. C# compiler uses any restriction at the generic definition to allow operation on generic argument. So, answer on the first question – Yes, it does check generic argument type. During definition of regular type based on generic type compiler checks if the type satisfies all restrictions and if so generate another type, internal one, for using with the combination of generic type and arguments. The compiler will use that generated type for all other instances of combination generic type and its argument type, but this, second phase, happened at runtime when request for creating of particular generic type with argument.
When type is created it is just regular, not generic type event the base class of it is generic and compiler uses common approach to check types related to any instance of this type.

Why is specialization in C# generics limited?

The question "What is reification?" has a comment on C#'s generics:
Type information is maintained, which allows specialization to an extent, by examining type arguments using reflection. However, the degree of specialization is limited, as a result of the fact that a generic type definition is compiled before any reification happens (this is done by compiling the definition against the constraints on the type parameters - thus, the compiler has to be able "understand" the definition even in the absence of specific type arguments).
What does it mean by "specialization"? Is it not the same as instantiation of a generic type with a specific type argument?
What does it mean by "the degree of specialization is limited"?
Why is it "a result of the fact that a generic type definition is compiled before any reification happens"?
What does it mean by "specialization"? Is it not the same as instantiation of a generic type with a specific type argument?
Author explains in the portion of his answer dedicated to Java generics that
specialization of a generic type [is] the ability to use specialized source code for any particular generic argument combination.
In other words, it is an ability to do something special if a generic type parameter is of a specific type. Supplying an implementation of List<T> that represents individual elements as bits when you instantiate the type as List<bool> would be an example of specialization.
What does it mean by "the degree of specialization is limited"?
Author means that although you can write things like
if (typeof(T) == typeof(bool)) {
...
}
your abilities to respond to a combination of type arguments are limited, because any decision on a type combination has to be made at run-time.
Why is it "a result of the fact that a generic type definition is compiled before any reification happens"?
Because reification is done in CLR, well after C# compiler is out of the picture. The compiler must produce a generic type definition for CLR to use as a "template" for making closed constructed types for instances of a generic class.
I believe the meaning is as follows:
When you define a generic type e.g. MyGenericType<T> your definition has to make sense for any value of T, as the generic type is compiled before you actually use it in a specific implementation ("the degree of specialization is limited, as a result of the fact that a generic type definition is compiled before any reification happens").
Later on, when you actually use a MyGenericType<int> the compiler/jit will create a new class which is pretty much MyGenericType<T> with every mention of T replaced with int. This is the process of reification. This means that at runtime, you can use the fact that the generic type is using an int, but your ability to make use of this (specialisation) is limited, since when you defined MyGenericType<T> you didn't know this.
Specialization is used as antonym to generalization. When you created a generic type, you generalized a type definition. When you initialized it with a type, you specialized the compiled generic type to be able to create object of the type at run-time.
IL compiles the generic type. At runtime, this compiled generic type is combined with specific type argument to produce an object of the specified class.
Yes, specialization is same as instantiation of a generic type with a specific type argument at runtime.
With generics, come constraints which basically fix the scope of generic type. You can tell that by defining that T can be a struct, class, or has to have some specific base class etc. You cannot create a class instance which is not allowed by the constraints defined on the generic type.
You can initialize the same generic type definition with a int, string or another class, if it satisfied the constraints in the generic class.
It cannot directly create an object of the class with T, not yet replaced by a defined type (primitive types like int, string, or your custom class or interface) and your code inside should be compatible to type being passed in as T for it to work.
Refer (Links from same question you mentioned above):
NET Generics and Code Bloat
Generics are not templates (as in C++)

What is the proper terminology for each type of an identifier?

Take the following code:
IFoo foo = new FooImplementation();
The identifier foo has two types:
IFoo - This is the type the compiler will enforce. I will only be able to call methods that are part of the IFoo contract, otherwise I'll get a compiler error.
FooImplementation - This is the type as known by the runtime. I can downcast foo to a FooImplementation at runtime, and then call non-IFoo methods of FooImplementation.
My question: What is the proper terminology for these two types. I could swear in school we were taught that IFoo is the identifier's static type and FooImplementation is its dynamic type, but after much searching on Google I can't seem to find any reference to this.
I would call IFoo and FooImplementation the compile-time and run-time types, respectively. This language is used by C# spec, for example, when talking about virtual methods (section 1.6.6.4):
When a virtual method is invoked, the run-time type of the instance for which that invocation takes place determines the actual method implementation to invoke. In a nonvirtual method invocation, the compile-time type of the instance is the determining factor.
I agree with Mike Z. The usual terminology in C# is "compile time type" and "runtime type".
"Static type" and "dynamic type" are entirely reasonable terms but I would avoid them in the context of C#. "Static type" could too easily be confused with "static class", a class which can only contain static methods. And "dynamic type" can too easily be confused with the dynamic type feature added to C# 4.
The first is the Declared Type. The second is the Concrete Type.
...at least that's what I call them.

Constraint which force generic parameter to be parseable from string

In this topic one can find solution to make generic class which provides parsing from string to generic parameter type. However, it could happen that type given to this parameter does not provide such conversion. Is there a way to make constraint assuring that?
UPDATE:
Thank you for your answers. However I know I can generally make constraints. The problem is: is there any interface that all types, which Convert.ChangeType won't fail at, implements. Or which is implemented by all types with Parse(string) method.
Since, as I said in my comment, you can't add interfaces to specific types nor can you use duck-typing in generic type constraints, you'll probably have to end up writing a bit of a hacky solution. I think your best bet is to make several overloads of your parsing function, one for each of the basic types (int, char, string, etc.) and then provide a generic one that has IConvertible as its generic constraint. The compiler will choose the right overload, or none at all in the case of non-convertible types.
You need to use Type Constraints to restrict what types of objects can be used in the generic class. You should define an interface that can be used to do that conversion and than set that interface as the constraint.

C# type parameters specification

Some special CLI types from mscorlib library (ArgIterator, TypedReference and RuntimeArgumentHandle types) cannot be used as generic type parameters to construct the generic types / methods:
void Foo<T>() { }
void Bar() { Foo<ArgIterator>(); }
provides the compiler error:
error CS0306: The type 'System.ArgIterator' may not be used as a type argument
But this is not documented at all in the C# specification.
Is this types are a part of CLI specification or this types provided by CLR implementation and the behavior described above should not be documented at C# spec?
First off, Jon is again correct -- these guys are very special types whose values are not convertible to object, and so cannot be used as type arguments. All type arguments must be types whose values are convertible to object.
To answer your question about documentation:
None of the special features for handling variadic methods are documented. They are not a part of the C# language itself -- a conforming implementation of the language is not required to be able to do interop with languages that support variadic methods. Nor are these features documented in MSDN as part of the compiler documentation. These are not "officially supported" features.
This is unfortunate, but there's only so much budget available, and I think most people would agree that we'd do better to write features and fix bugs than to spend money documenting features that literally 99.99% of our users will never, ever use even if they were supported, which they're not.
If you want to go do interop in C# with variadic methods, you're on your own. Good luck!
I believe it's because these types are "special" in that they can't be converted to object; only types which can be converted to object can be specified as type arguments. The same is true for pointers, by the way.
I can't find where this is documented (it's documented for pointers in 4.4.1) but Eric Lippert mentioned it in a comment the other day.
Is this just a matter of interest, or are you trying to actually do something using this kind of thing?
All three of the examples that you provided are structs, and not classes, so I suspect that's the key to the problem. An example provided in the documentation on the compiler error message indicates also that if you use a pointer to a type in the generic it would fail.
ArgIterator
RuntimeArgumentHandle
TypedReference
Section 8.2.4 of the CLI spec calls value types which can contain pointers into the evaluation stack "byref-like" types and says that they cannot be boxed. It explicitly calls out System.RuntimeArgumentHandle and System.TypedReference as examples of such types but does not provide an exhaustive list. Section 9.4 goes on to state that byref types, byref-like types, and System.Void cannot be used to instantiate generic types or methods.
Just as a comment, here's some more fun you can have when trying to compile code with this type which is not convertible to object. All of the methods here come up as suggestions by Visual Studio when you type the . (dot).
ArgIterator.ReferenceEquals(new object(), new object()); // OK; static method inherited from System.Object
var strange = default(ArgIterator);
strange.End(); // OK; non-virtual method defined in the struct
strange.GetHashCode(); // OK; method overridden in the struct
strange.ToString(); // compile-time error; method overriden in System.ValueType, inherited but not overridden in the struct
strange.GetType(); // compile-time error; non-virtual method inherited from System.Object

Categories