I know that Java implements parametric polymorphism (Generics) with erasure. I understand what erasure is.
I know that C# implements parametric polymorphism with reification. I know that can make you write
public void dosomething(List<String> input) {}
public void dosomething(List<Int> input) {}
or that you can know at runtime what the type parameter of some parameterised type is, but I don't understand what it is.
What is a reified type?
What is a reified value?
What happens when a type/value is reified?
Reification is the process of taking an abstract thing and creating a concrete thing.
The term reification in C# generics refers to the process by which a generic type definition and one or more generic type arguments (the abstract thing) are combined to create a new generic type (the concrete thing).
To phrase it differently, it is the process of taking the definition of List<T> and int and producing a concrete List<int> type.
To understand it further, compare the following approaches:
In Java generics, a generic type definition is transformed to essentially one concrete generic type shared across all allowed type argument combinations. Thus, multiple (source code level) types are mapped to one (binary level) type - but as a result, information about the type arguments of an instance is discarded in that instance (type erasure).
As a side effect of this implementation technique, the only generic type arguments that are natively allowed are those types that can share the binary code of their concrete type; which means those types whose storage locations have interchangeable representations; which means reference types. Using value types as generic type arguments requires boxing them (placing them in a simple reference type wrapper).
No code is duplicated in order to implement generics this way.
Type information that could have been available at runtime (using reflection) is lost. This, in turn, means that specialization of a generic type (the ability to use specialized source code for any particular generic argument combination) is very restricted.
This mechanism doesn't require support from the runtime environment.
There are a few workarounds to retain type information that a Java program or a JVM-based language can use.
In C# generics, the generic type definition is maintained in memory at runtime. Whenever a new concrete type is required, the runtime environment combines the generic type definition and the type arguments and creates the new type (reification). So we get a new type for each combination of the type arguments, at runtime.
This implementation technique allows any kind of type argument combination to be instantiated. Using value types as generic type arguments does not cause boxing, since these types get their own implementation. (Boxing still exists in C#, of course - but it happens in other scenarios, not this one.)
Code duplication could be an issue - but in practice it isn't, because sufficiently smart implementations (this includes Microsoft .NET and Mono) can share code for some instantiations.
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).
This implementation technique depends heavily on runtime support and JIT-compilation (which is why you often hear that C# generics have some limitations on platforms like iOS, where dynamic code generation is restricted).
In the context of C# generics, reification is done for you by the runtime environment. However, if you want to more intuitively understand the difference between a generic type definition and a concrete generic type, you can always perform a reification on your own, using the System.Type class (even if the particular generic type argument combination you're instantiating didn't appear in your source code directly).
In C++ templates, the template definition is maintained in memory at compile time. Whenever a new instantiation of a template type is required in the source code, the compiler combines the template definition and the template arguments and creates the new type. So we get a unique type for each combination of the template arguments, at compile time.
This implementation technique allows any kind of type argument combination to be instantiated.
This is known to duplicate binary code but a sufficiently smart tool-chain could still detect this and share code for some instantiations.
The template definition itself is not "compiled" - only its concrete instantiations are actually compiled. This places fewer constraints on the compiler and allows a greater degree of template specialization.
Since template instantiations are performed at compile time, no runtime support is needed here either.
This process is lately referred to as monomorphization, especially in the Rust community. The word is used in contrast to parametric polymorphism, which is the name of the concept that generics come from.
Reification means generally (outside of computer science) "to make something real".
In programming, something is reified if we're able to access information about it in the language itself.
For two completely non-generics-related examples of something C# does and doesn't have reified, let's take methods and memory access.
OO languages generally have methods, (and many that don't have functions that are similar though not bound to a class). As such you can define a method in such a language, call it, perhaps override it, and so on. Not all such languages let you actually deal with the method itself as data to a program. C# (and really, .NET rather than C#) does let you make use of MethodInfo objects representing the methods, so in C# methods are reified. Methods in C# are "first class objects".
All practical languages have some means to access the memory of a computer. In a low-level language like C we can deal directly with the mapping between numeric addresses used by the computer, so the likes of int* ptr = (int*) 0xA000000; *ptr = 42; is reasonable (as long as we've a good reason to suspect that accessing memory address 0xA000000 in this way won't blow something up). In C# this isn't reasonable (we can just about force it in .NET, but with the .NET memory management moving things around it's not very likely to be useful). C# does not have reified memory addresses.
So, as refied means "made real" a "reified type" is a type we can "talk about" in the language in question.
In generics this means two things.
One is that List<string> is a type just as string or int are. We can compare that type, get its name, and enquire about it:
Console.WriteLine(typeof(List<string>).FullName); // System.Collections.Generic.List`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
Console.WriteLine(typeof(List<string>) == (42).GetType()); // False
Console.WriteLine(typeof(List<string>) == Enumerable.Range(0, 1).Select(i => i.ToString()).ToList().GetType()); // True
Console.WriteLine(typeof(List<string>).GenericTypeArguments[0] == typeof(string)); // True
A consequence of this is that we can "talk about" a generic method's (or method of a generic class) parameters' types within the method itself:
public static void DescribeType<T>(T element)
{
Console.WriteLine(typeof(T).FullName);
}
public static void Main()
{
DescribeType(42); // System.Int32
DescribeType(42L); // System.Int64
DescribeType(DateTime.UtcNow); // System.DateTime
}
As a rule, doing this too much is "smelly", but it has many useful cases. For example, look at:
public static TSource Min<TSource>(this IEnumerable<TSource> source)
{
if (source == null) throw Error.ArgumentNull("source");
Comparer<TSource> comparer = Comparer<TSource>.Default;
TSource value = default(TSource);
if (value == null)
{
using (IEnumerator<TSource> e = source.GetEnumerator())
{
do
{
if (!e.MoveNext()) return value;
value = e.Current;
} while (value == null);
while (e.MoveNext())
{
TSource x = e.Current;
if (x != null && comparer.Compare(x, value) < 0) value = x;
}
}
}
else
{
using (IEnumerator<TSource> e = source.GetEnumerator())
{
if (!e.MoveNext()) throw Error.NoElements();
value = e.Current;
while (e.MoveNext())
{
TSource x = e.Current;
if (comparer.Compare(x, value) < 0) value = x;
}
}
}
return value;
}
This doesn't do lots of comparisons between the type of TSource and various types for different behaviours (generally a sign that you shouldn't have used generics at all) but it does split between a code path for types that can be null (should return null if no element found, and must not make comparisons to find the minimum if one of the elements compared is null) and the code path for types that cannot be null (should throw if no element found, and doesn't have to worry about the possibility of null elements).
Because TSource is "real" within the method, this comparison can be made either at runtime or jitting time (generally at jitting time, and certainly the above case would do so at jitting time and not produce machine code for the path not taken) and we have a separate "real" version of the method for each case. (Though as an optimisation, the machine code is shared for different methods for different reference-type type parameters, because it can be without affecting this, and hence we can reduce the amount of machine code jitted.)
(It's not common to talk about reification of generic types in C# unless you also deal with Java, because in C# we just take this reification for granted; all types are reified. In Java, non-generic types are referred to as reified because that is a distinction between them and generic types.)
As duffymo already noted, "reification" isn't the key difference.
In Java, generics are basically there to improve compile-time support - it allows you to use strongly typed e.g. collections in your code, and have type safety handled for you. However, this only exists at compile-time - the compiled bytecode no longer has any notion of generics; all the generic types are transformed into "concrete" types (using object if the generic type is unbounded), adding type conversions and type checks as needed.
In .NET, generics are an integral feature of the CLR. When you compile a generic type, it stays generic in the generated IL. It's not just transformed into non-generic code as in Java.
This has several impacts on how generics work in practice. For example:
Java has SomeType<?> to allow you to pass any concrete implementation of a given generic type. C# cannot do this - every specific (reified) generic type is its own type.
Unbounded generic types in Java mean that their value is stored as an object. This can have a performance impact when using value types in such generics. In C#, when you use a value type in a generic type, it stays a value type.
To give a sample, let's suppose you have a List generic type with one generic argument. In Java, List<String> and List<Int> will end up being the exact same type at runtime - the generic types only really exist for compile-time code. All calls to e.g. GetValue will be transformed to (String)GetValue and (Int)GetValue respectively.
In C#, List<string> and List<int> are two different types. They are not interchangeable, and their type-safety is enforced in runtime as well. No matter what you do, new List<int>().Add("SomeString") will never work - the underlying storage in List<int> is really some integer array, while in Java, it is necessarily an object array. In C#, there are no casts involved, no boxing etc.
This should also make it obvious why C# can't do the same thing as Java with SomeType<?>. In Java, all generic types "derived from" SomeType<?> end up being the exact same type. In C#, all the various specific SomeType<T>s are their own separate type. Removing compile-time checks, it's possible to pass SomeType<Int> instead of SomeType<String> (and really, all that SomeType<?> means is "ignore compile-time checks for the given generic type"). In C#, it's not possible, not even for derived types (that is, you can't do List<object> list = (List<object>)new List<string>(); even though string is derived from object).
Both implementations have their pros and cons. There's been a few times when I'd have loved to be able to just allow SomeType<?> as an argument in C# - but it simply doesn't make sense the way C# generics work.
Reification is an object-oriented modeling concept.
Reify is a verb that means "make something abstract real".
When you do object oriented programming it's common to model real world objects as software components (e.g. Window, Button, Person, Bank, Vehicle, etc.)
It's also common to reify abstract concepts into components as well (e.g. WindowListener, Broker, etc.)
Related
class Foo<T>
{
public T Bar() { /* ... */ }
}
I'd like to pass Bar's name to Type.GetMethod(string). I can do this as someType.GetMethod(nameof(Foo<int>.Bar)), but that int is wholly arbitrary here; is there any way I can omit it? Sadly, nameof(Foo<>.Bar) doesn't work.
It's not such a big deal in this toy case, but if there are multiple type parameters and especially if they have where constraints attached to them, spelling them all out can become a task.
The nameof documentation specifically says that what you want to do is disallowed, unfortunately:
Because the argument needs to be an expression syntactically, there
are many things disallowed that are not useful to list. The following
are worth mentioning that produce errors: predefined types (for
example, int or void), nullable types (Point?), array types
(Customer[,]), pointer types (Buffer*), qualified alias (A::B), and
unbound generic types (Dictionary<,>), preprocessing symbols (DEBUG),
and labels (loop:).
The best you can probably do is specify Bar in an interface, and use nameof(IFoo.Bar). Of course, that isn't an option if Bar includes something related to T in its signature (like in this specific case).
Another option is to create an interface where each T is replaced with object. Then the concrete type implements the interface explicitly, also implementing generic versions of the same methods.
This has a few downsides:
Larger API surface
More difficult and error-prone refactoring
Losing compile-time type safety because a caller might use the object interface.
This probably isn't justifiable just to use nameof, but in some cases this strategy makes sense for other reasons. In those cases, being able to use nameof would just be a convenient bonus.
Currently, I'm reading C# 5.0 in a Nutshell and in the Type Basics (located at Chapter 2) subtopic the term symmetry of predefined types and custom types is introduced...
...why the author talks about symmetry between those types? What are the argument to say that types are symmetric?
This is the original paragraph:
A beautiful aspect of C# is that predefined types and custom types have few differences. The predefined int type serves as a blueprint for integers. It holds data -32 bits- and provides function members that use that data, such as ToString. Similarly, our custom UnitConverter type acts as a blueprint for unit conversions. It holds data -the ratio- and provides function members to use that data.
The meaning of the text is that all of the types in the language "behave like objects". For example, you can do this:
int i = 42;
Console.WriteLine(i.ToString()); // called a method on an int
as easily as you can do this:
DateTime d = DateTime.Now;
Console.WriteLine(d.ToString()); // called a method on a DateTime
That is, if you did not know the types of i and d you would not be able to deduce anything by just reading the code that calls .ToString().
Contrast this with a language like C++:
std::string s = "hello";
std::cout << s.length();
int i = 42;
// ...but you cannot invoke any method on i; "ints are special"
Here the fact that s.length() compiles tells us that s is not an int, or a pointer, or any other primitive type.
This uniformity does not buy you anything from a technical standpoint, but it is nevertheless a welcome feature -- especially if you are just learning the language.
I believe the author's point is that built-in types and custom types are both meant to serve similar purposes; hold data and provide mechanisms for using that data. I believe there is also a veiled reference in there that both built-in objects and custom objects inherit from System.Object, thus they share similar abilities (i.e. ToString()).
I do not necessarily agree with term 'symmetry', but that is my two cents on the topic.
There are languages where you can't create type that behave similarly to something that is built in like int. In C# one can create type with all properties of 'int' (which is synonym of System.Int32) - value type with +/-/=operation.
I.e. in JavaScript one can't create another Array type or string type. In Java value types are (were?) not present, so type equivalent to 'int' can't be written by programmer ( Does Java make distinction between value type and reference type ).
I am reading the latest edition of this book for C# 10 and had the same question. I like all of the answers so far provided, but have one additional point: this is a reference to the utility of C#'s unified type system, which the author mentions briefly in Chapter 1 under the section 'Object Orientation':
Unified type system
The fundamental building block in C# is an encapsulated unit of data and functions called a type. C# has a unified type system in which all types ultimately share a common base type. This means that all types, whether they represent business objects or are primitive types such as numbers, share the same basic functionality. For example, an instance of any type can be converted to a string by calling its ToString() method.
To distill the author's writing and the comments from others down into a few points:
The type system of C# is unified, in that all types inherit from the common System.Object type.
Logically, type components can be logically grouped into two components: data members and function members.
Practically, because types behave like objects (they all have some data and/or behavior); developers can expect a minimum set of functionality from all types when using C#, such as the ability to call .ToString(), among other things.
These three points seem to be what the author had in mind when he referred to the C# type system having a 'symmetric beauty'.
As discovered in C 3.5, the following would not be possible due to type erasure: -
int foo<T>(T bar)
{
return bar.Length; // will not compile unless I do something like where T : string
}
foo("baz");
I believe the reason this doesn't work is in C# and java, is due to a concept called type erasure, see http://en.wikipedia.org/wiki/Type_erasure.
Having read about the dynamic keyword, I wrote the following: -
int foo<T>(T bar)
{
dynamic test = bar;
return test.Length;
}
foo("baz"); // will compile and return 3
So, as far as I understand, dynamic will bypass compile time checking but if the type has been erased, surely it would still be unable to resolve the symbol unless it goes deeper and uses some kind of reflection?
Is using the dynamic keyword in this way bad practice and does this make generics a little more powerful?
dynamics and generics are 2 completely different notions. If you want compile-time safety and speed use strong typing (generics or just standard OOP techniques such as inheritance or composition). If you do not know the type at compile time you could use dynamics but they will be slower because they are using runtime invocation and less safe because if the type doesn't implement the method you are attempting to invoke you will get a runtime error.
The 2 notions are not interchangeable and depending on your specific requirements you could use one or the other.
Of course having the following generic constraint is completely useless because string is a sealed type and cannot be used as a generic constraint:
int foo<T>(T bar) where T : string
{
return bar.Length;
}
you'd rather have this:
int foo(string bar)
{
return bar.Length;
}
I believe the reason this doesn't work is in C# and java, is due to a concept called type erasure, see http://en.wikipedia.org/wiki/Type_erasure.
No, this isn't because of type erasure. Anyway there is no type erasure in C# (unlike Java): a distinct type is constructed by the runtime for each different set of type arguments, there is no loss of information.
The reason why it doesn't work is that the compiler knows nothing about T, so it can only assume that T inherits from object, so only the members of object are available. You can, however, provide more information to the compiler by adding a constraint on T. For instance, if you have an interface IBar with a Length property, you can add a constraint like this:
int foo<T>(T bar) where T : IBar
{
return bar.Length;
}
But if you want to be able to pass either an array or a string, it won't work, because the Length property isn't declared in any interface implemented by both String and Array...
No, C# does not have type erasure - only Java has.
But if you specify only T, without any constraint, you can not use obj.Lenght because T can virtually be anything.
foo(new Bar());
The above would resolve to an Bar-Class and thus the Lenght Property might not be avaiable.
You can only use Methods on T when you ensure that T this methods also really has. (This is done with the where Constraints.)
With the dynamics, you loose compile time checking and I suggest that you do not use them for hacking around generics.
In this case you would not benefit from dynamics in any way. You just delay the error, as an exception is thrown in case the dynamic object does not contain a Length property. In case of accessing the Length property in a generic method I can't see any reason for not constraining it to types who definately have this property.
"Dynamics are a powerful new tool that make interop with dynamic languages as well as COM easier, and can be used to replace much turgid reflective code. They can be used to tell the compiler to execute operations on an object, the checking of which is deferred to runtime.
The great danger lies in the use of dynamic objects in inappropriate contexts, such as in statically typed systems, or worse, in place of an interface/base class in a properly typed system."
Qouted From Article
Thought I'd weigh-in on this one, because no one clarified how generics work "under the hood". That notion of T being an object is mentioned above, and is quite clear. What is not talked about, is that when we compile C# or VB or any other supported language, - at the Intermediate Language (IL) level (what we compile to) which is more akin to an assembly language or equivalent of Java Byte codes, - at this level, there is no generics! So the new question is how do you support generics in IL? For each type that accesses the generic, a non-generic version of the code is generated which substitutes the generic(s) such as the ubiquitous T to the actual type it was called with. So if you only have one type of generic, such as List<>, then that's what the IL will contain. But if you use many implementation of a generic, then many specific implementations are created, and calls to the original code substituted with the calls to the specific non-generic version. To be clear, a MyList used as: new MyList(), will be substituted in IL with something like MyList_string().
That's my (limited) understanding of what's going on. The point being, the benefit of this approach is that the heavy lifting is done at compile-time, and at runtime there's no degradation to performance - which is again, why generic are probably so loved used anywhere, and everywhere by .NET developers.
On the down-side? If a method or type is used many times, then the output assembly (EXE or DLL) will get larger and larger, dependent of the number of different implementation of the same code. Given the average size of DLLs output - I doubt you'll ever consider generics to be a problem.
If I have a generic interface with a covariant type parameter, like this:
interface IGeneric<out T>
{
string GetName();
}
And If I define this class hierarchy:
class Base {}
class Derived1 : Base{}
class Derived2 : Base{}
Then I can implement the interface twice on a single class, like this, using explicit interface implementation:
class DoubleDown: IGeneric<Derived1>, IGeneric<Derived2>
{
string IGeneric<Derived1>.GetName()
{
return "Derived1";
}
string IGeneric<Derived2>.GetName()
{
return "Derived2";
}
}
If I use the (non-generic)DoubleDown class and cast it to IGeneric<Derived1> or IGeneric<Derived2> it functions as expected:
var x = new DoubleDown();
IGeneric<Derived1> id1 = x; //cast to IGeneric<Derived1>
Console.WriteLine(id1.GetName()); //Derived1
IGeneric<Derived2> id2 = x; //cast to IGeneric<Derived2>
Console.WriteLine(id2.GetName()); //Derived2
However, casting the x to IGeneric<Base>, gives the following result:
IGeneric<Base> b = x;
Console.WriteLine(b.GetName()); //Derived1
I expected the compiler to issue an error, as the call is ambiguous between the two implementations, but it returned the first declared interface.
Why is this allowed?
(inspired by A class implementing two different IObservables?. I tried to show to a colleague that this will fail, but somehow, it didn't)
If you have tested both of:
class DoubleDown: IGeneric<Derived1>, IGeneric<Derived2> {
string IGeneric<Derived1>.GetName() {
return "Derived1";
}
string IGeneric<Derived2>.GetName() {
return "Derived2";
}
}
class DoubleDown: IGeneric<Derived2>, IGeneric<Derived1> {
string IGeneric<Derived1>.GetName() {
return "Derived1";
}
string IGeneric<Derived2>.GetName() {
return "Derived2";
}
}
You must have realized that the results in reality, changes with the order you declaring the interfaces to implement. But I'd say it is just unspecified.
First off, the specification(§13.4.4 Interface mapping) says:
If more than one member matches, it is unspecified which member is the implementation of I.M.
This situation can only occur if S is a constructed type where the two members as declared in the generic type have different signatures, but the type arguments make their signatures identical.
Here we have two questions to consider:
Q1: Do your generic interfaces have different signatures?
A1: Yes. They are IGeneric<Derived2> and IGeneric<Derived1>.
Q2: Could the statement IGeneric<Base> b=x; make their signatures identical with type arguments?
A2: No. You invoked the method through a generic covariant interface definition.
Thus your call meets the unspecified condition. But how could this happen?
Remember, whatever the interface you specified to refer the object of type DoubleDown, it is always a DoubleDown. That is, it always has these two GetName method. The interface you specify to refer it, in fact, performs contract selection.
The following is the part of captured image from the real test
This image shows what would be returned with GetMembers at runtime. In all cases you refer it, IGeneric<Derived1>, IGeneric<Derived2> or IGeneric<Base>, are nothing different. The following two image shows more details:
As the images shown, these two generic derived interfaces have neither the same name nor another signatures/tokens make them identical.
The compiler can't throw an error on the line
IGeneric<Base> b = x;
Console.WriteLine(b.GetName()); //Derived1
because there is no ambiguity that the compiler can know about. GetName() is in fact a valid method on interface IGeneric<Base>. The compiler doesn't track the runtime type of b to know that there is a type in there which could cause an ambiguity. So it's left up to the runtime to decide what to do. The runtime could throw an exception, but the designers of the CLR apparently decided against that (which I personally think was a good decision).
To put it another way, let's say that instead you simply had written the method:
public void CallIt(IGeneric<Base> b)
{
string name = b.GetName();
}
and you provide no classes implementing IGeneric<T> in your assembly. You distribute this and many others implement this interface only once and are able to call your method just fine. However, someone eventually consumes your assembly and creates the DoubleDown class and passes it into your method. At what point should the compiler throw an error? Surely the already compiled and distributed assembly containing the call to GetName() can't produce a compiler error. You could say that the assignment from DoubleDown to IGeneric<Base> produces the ambiguity. but once again we could add another level of indirection into the original assembly:
public void CallItOnDerived1(IGeneric<Derived1> b)
{
return CallIt(b); //b will be cast to IGeneric<Base>
}
Once again, many consumers could call either CallIt or CallItOnDerived1 and be just fine. But our consumer passing DoubleDown also is making a perfectly legal call that could not cause a compiler error when they call CallItOnDerived1 as converting from DoubleDown to IGeneric<Derived1> should certainly be OK. Thus, there is no point at which the compiler can throw an error other than possibly on the definition of DoubleDown, but this would eliminate the possibility of doing something potentially useful with no workaround.
I have actually answered this question more in depth elsewhere, and also provided a potential solution if the language could be changed:
No warning or error (or runtime failure) when contravariance leads to ambiguity
Given that the chance of the language changing to support this is virtually zero, I think that the current behavior is alright, except that it should be laid out in the specifications so that all implementations of the CLR would be expected to behave the same way.
Holy goodness, lots of really good answers here to what is quite a tricky question. Summing up:
The language specification does not clearly say what to do here.
This scenario usually arises when someone is attempting to emulate interface covariance or contravariance; now that C# has interface variance we hope that less people will use this pattern.
Most of the time "just pick one" is a reasonable behaviour.
How the CLR actually chooses which implementation is used in an ambiguous covariant conversion is implementation-defined. Basically, it scans the metadata tables and picks the first match, and C# happens to emit the tables in source code order. You can't rely on this behaviour though; either can change without notice.
I'd only add one other thing, and that is: the bad news is that interface reimplementation semantics do not exactly match the behaviour specified in the CLI specification in scenarios where these sorts of ambiguities arise. The good news is that the actual behaviour of the CLR when re-implementing an interface with this kind of ambiguity is generally the behaviour that you'd want. Discovering this fact led to a spirited debate between me, Anders and some of the CLI spec maintainers and the end result was no change to either the spec or the implementation. Since most C# users do not even know what interface reimplementation is to begin with, we hope that this will not adversely affect users. (No customer has ever brought it to my attention.)
The question asked, "Why doesn't this produce a compiler warning?".
In VB, it does(I implemented it).
The type system doesn't carry enough information to provide a warning at time of invocation about variance ambiguity. So the warning has to be emitted earlier ...
In VB, if you declare a class C which implements both IEnumerable(Of Fish) and IEnumerable(Of Dog), then it gives a warning saying that the two will conflict in the common case IEnumerable(Of Animal). This is enough to stamp out variance-ambiguity from code that's written entirely in VB.
However, it doesn't help if the problem class was declared in C#. Also note that it's completely reasonable to declare such a class if no one invokes a problematic member on it.
In VB, if you perform a cast from such a class C into IEnumerable(Of Animal), then it gives a warning on the cast. This is enough to stamp out variance-ambiguity even if you imported the problem class from metadata.
However, it's a poor warning location because it's not actionable: you can't go and change the cast. The only actionable warning to people would be to go back and change the class definition. Also note that it's completely reasonable to perform such a cast if no one invokes a problematic member on it.
Question:
How come VB emits these warnings but C# doesn't?
Answer:
When I put them into VB, I was enthusiastic about formal computer science, and had only been writing compilers for a couple of years, and I had the time and enthusiasm to code them up.
Eric Lippert was doing them in C#. He had the wisdom and maturity to see that coding up such warnings in the compiler would take a lot of time that could be better spent elsewhere, and was sufficiently complex that it carried high risk. Indeed the VB compilers had bugs in these very warnings that were only fixed in VS2012.
Also, to be frank, it was impossible to come up with a warning message useful enough that people would understand it. Incidentally,
Question:
How does the CLR resolve the ambiguity when chosing which one to invoke?
Answer:
It bases it on the lexical ordering of inheritance statements in the original source code, i.e. the lexical order in which you declared that C implements IEnumerable(Of Fish) and IEnumerable(Of Dog).
Trying to delve into the "C# language specifications", it looks that the behaviour is not specified (if I did not get lost in my way).
7.4.4 Function member invocation
The run-time processing of a function member invocation consists of the following steps, where M is the function member and, if M is an instance member, E is the instance expression:
[...]
o The function member implementation to invoke is determined:
• If the compile-time type of E is an interface, the function member to invoke is the implementation of M provided by the run-time type of the instance referenced by E. This function member is determined by applying the interface mapping rules (§13.4.4) to determine the implementation of M provided by the run-time type of the instance referenced by E.
13.4.4 Interface mapping
Interface mapping for a class or struct C locates an implementation for each member of each interface specified in the base class list of C. The implementation of a particular interface member I.M, where I is the interface in which the member M is declared, is determined by examining each class or struct S, starting with C and repeating for each successive base class of C, until a match is located:
• If S contains a declaration of an explicit interface member implementation that matches I and M, then this member is the implementation of I.M.
• Otherwise, if S contains a declaration of a non-static public member that matches M, then this member is the implementation of I.M. If more than one member matches, it is unspecified which member is the implementation of I.M. This situation can only occur if S is a constructed type where the two members as declared in the generic type have different signatures, but the type arguments make their signatures identical.
One of the benefits with C++ templates is that you (implicitly) can require a certain type signature (e.g. type T needs to have a function x that takes no parameters and returns an int). Does C# generics support something similar?
I am aware of constraints based on base class or interface but this is not what I am looking for.
(As a C++ programmer learning C# I might be mistaken that this is a feature that you would want in C#. Any comments on this would be appriciated as well...)
Nothing except for the constraints you have already seen (which do, to be fair, cover a lot of common scenarios). There are some common workarounds:
dynamic, in 4.0
manual duck-typing using either reflection or IL generation etc
none of these have static type-checking etc, though.
Yes, through an interface. You can define a generic object that has a type that must have a specific interface implemented. Within that interface, you would essentially be forcing any object added to that generic, list for instance, to have a specific signature.
Whether or not that's what you're not looking for, that's how you accomplish it. :)
No, this is not possible. It's mainly caused by the differences between C++ templates and C# generics:
When you compile C++ template, the resulting code has types like vector<int> and vector<string>. This means the compiler has to know all the possible type parameters, but it also means it can check them for correctness.
When you compile C# generic type, you are actually creating just one generic type: List<T>. Because of this, the C# compiler doesn't have to know all the possible types at compile type, which means you can have generic types in binary libraries, which is not possible with C++. But this also means that you can't check all the types. To be able to do something like that, there are constraints, but they can't do several things C++'s compile time checking can, like checking the presence of certain methods (without using interface or some base class) or the presence of suitable operators.
In C# 4, you can achieve effect somewhat similar to this kind of templates using dynamic, but this does no compile-time checking, which means you lose safety – you can put in a type that doesn't have the appropriate members and you won't find out until you reach that line of code at runtime.
No. This is what interfaces are for. Create an interface that defined the contract you want to enforce in the type constraints. Then specify that in the constraints.
Nope, not supported in c#. Like you said, the closest thing requires you to have the classes implement a common interface.
You could try to mimic the behavior with reflection, by looking for the method by the signature, but that's a runtime constraint, and not a compile-time constraint.
There are 5 types of constraints you can put onto generics in .Net:
Derivation constraints state the ascendancy of a type parameter.
Interface constraints are interfaces that are implemented by the type parameter.
Value type constraints restrict a type parameter to a value type.
Reference type constraints restrict a type parameter to a reference type.
Constructor constraints stipulate that the type parameter has a default or parameterless constructor.
This page shows more information.
No, C# does not have constraints like that.
As you know, generic constraints can only enforce inheritance of a base class or an interface, or a few other constraints (constructor constraint new(), reference type constraint class, value type constraint struct).
You might be able to achieve your desired behavior using delegates, and there are many Generic delegates available.
For example, Func<int> is a delegate that takes no parameters and returns an int. Func<string, DateTime, int> takes a string and DateTime and returns an int, etc...