using System; using System.Collections.Generic; #pragma warning disable 169, 219, 414, 649 // above line disables warnings about variables declared but never used, // functions never called, and other harmless things. // let's look at a generic class in C# and compare with the similar one in Java: class generalclass where B:IComparable //where A:B { A x; B y; A[] ar; void f() { //y = (B)x; // compiled with warning in Java, COMPILER ERROR here //x = (A)y; // but will comple with declaration where A:B // no generic array creation in java without workaround: //ar = (A[])new object[10]; // compiler ERROR in C#, casting not allowed. ar = new A[10]; // But direct generic array creation is fine! List al = new List(); // also ok, List equivalent to ArrayList }// public static A z; // not a problem! get weird compiler error in java public B largest(B[] R) // find largest B value in array R: { B max = R[0]; for(int i=1;i0) max = R[i]; return max; }// this really is ONE polymorphic, type checked procedure, unlike in C++ // does overloading work with generic variables? (QUIZ!) public int h(A x) { return 1; } public int h(B x) { return 2; } // C# allows this overload to compile but... }// a c# version of a generic class that has none of java's problems public class generics { public static void Main() { generalclass n = new generalclass(); //int x = n.h("abc"); // which h is called? this is a COMPILER error new test().g(); // testing test class below }//main } /* Most of the differences between the behaviors of this program and the Java program is explained by the fact that C# does not use type erasure and change the type of x to object at runtime. So generalclass and generalclass REMAIN DISTINGUISHABLE at runtime. Each class constructor is created with hidden arguments that's instantiated at runtime. So new generalclass, for example, will instantiate A with int and B with string. There's no need to allow the cast (B)x because it might possibly work: it should be rejected because there's no compile-time information to justify it: A and B are statically unrelated classes unless we establish, as part of the generic class, that A:B (A is a subtype of B). Similarly, there's no need to allow the cast from object[] to A[] just because it might work There's no problem with declaring a static variable of generic type because each runtime class will still have it's own copy of the variable. However, the reason generic array creation A[] x = new A[10]; works is due to a hidden use of an interface. Generic arrays are NOT treated in the same way as regular arrays in C#: they are actually objects that implement the interface Ilist. So when we create generic arrays, it's like we're creating something like the List class underneath (except it can't be resized). The T type variable is invariant in Ilist. C# allows operator overloading, including the indexing operation [i], so syntactically it still feels like we're using a regular array, but it isn't. To proof this point: consider the following class: */ class test where B:A // B is a subclass of A { public void g() { object[] x = new string[10]; // we know this compiles but shouldn't //A[] z = new B[10]; //this should compile too then, right? because B:A? // But it's a compiler error, the same type of error that we get with: // List l = new List(); //object[] oo = new B[10]; This also does not compile, in contrast to // object[] x = new string[10]; // This is because in IList and List, A is INVARIANT. A[] n = new A[10]; Console.WriteLine( n is IList ); // prints True } } /* Another difference between C# and Java generics is that in java, only referenced types (objects) are allowed to instantiate a generic class and that's why you have types Integer, Double, etc in addition to int, double. C# retains the distinction between primitive types and objects at the generic level. When you instantiate a generic class with a primitive type, say , it creates a specialized version of the class (but not necessarily a new copy of the entire class like C++), because a bool is smaller than other types in size. It does not wrap the boolean inside an object like in java. However, in the above sample generic class, since we requrie that B:A, both B and A must be object (referenced) types, so there will only by one version of the code for any instantiation of the class. Other differences like this exists between C# and Java because C# trys to also support "low-cost abstractions", which just means it tries to be more efficient with regard to runtime performance when it can be. For an OOP-centric language, it's arguable whether this is really important. #### Finally, there is an issue that could potentially cause misunderstanding. Can you overload functions with a single generic variables? public int h(A x) { return 1; } public int h(B x) { return 2; } // C# allows this overload to compile but... If you search the web, there are people who say that this code "will not compile in java but will compile in C# because java uses type erasure so at runtime both A and B are of type Object." This is not entirely accurate. It's written by people who misunderstand the difference between overloading and overriding. Overloading resolves function calls at compile time and therefore has NOTHING TO DO WITH TYPE ERASURE. It's not allowed because you can write (look at main above): generalclass n = new generlclass(); int x = n.h("abc"); // this will cause a COMPILER ERROR in C# also. There's no way for the compiler to know in the above situation, how to resolve which version of h to call. Java does not allow the class with the two versions of h to compile. C# chooses to compile the class, but not the code that tries to use it in an ambiguous way. But this is more of a design choice than a fundamental difference: in both languages all calls to overloaded functions are resolved at COMPILE TIME. Overloading is not an essential feature and issues it causes can easily be avoided by: public int h1(A x) { return 1; } public int h2(B x) { return 2; } // all problems solved. */