Hello All,
one of my colleague - Damien Pinauldt - has proposed us a small exercices yesterday. After I have seen the unexpected results, I have been thinking it could be interesting to share it with you.
The goal of this exercices was to recall how method hiding was working in .NET and what can be the consequences when using generics.
Method Hiding
Let's first redefine what is a method hiding. Of course you all know that in .NET are virtuals only the methods you decide to be, on the opposite of the Java World where all are virtual.
As a consequence, you may be in front of cases where you want to "override" a "non-virtual" method. We agree, this is highly unadviced and a bad practice. However you can, and you may face from time to time this obligation.
So you can be tempted to write something like this :
public class A
{
public void DoSomething() { }
}
public class B : A
{
public new void DoSomething() { }
}
And you would be right. This is an example of method hiding. In such a case, Visual Studio compiles without any error but gives you a warning explaining that there is a name collision inside of the classes A and B. By default, VS understands such a writing as "Method B.DoSomething is hiding inherited method A.DoSomething".
To clarify your (bad) intention (and get rid of this warning), you should use the "new" keyword:
public class B : A
{
public new void DoSomething() {}
}
What is the consequence of such a code ?
Let's write an example and compare with polymorphism and method overriding:
public class A
{
public void DoSomething() { Console.WriteLine("In A"); }
public virtual void DoSomethingVirtual() { Console.WriteLine("In A"); }
}
public class B : A
{
public new void DoSomething() { Console.WriteLine("In B"); }
public override void DoSomethingVirtual() { Console.WriteLine("In B"); }
}
A a = new A();
a.DoSomething();
a.DoSomethingVirtual();
B b = new B();
b.DoSomething();
b.DoSomethingVirtual();
A a1 = new B();
a1.DoSomething();
a1.DoSomethingVirtual();
In a word, the method resolution is based on the static type (meaning the type of the variable) when using method hiding, and is based on the dynamic type when using polymorphism and method overriding.
I imagine I didn't learned you anything here.
Generics and Generated IL
Ok now with theses basis recalled, let's go now to generics.
We often compare .NET generics with C++ templates and Java generics. In fact if the goal is quite similar, they work in very different ways.
To simplify, we could say .NET is at the mid-way between C++ and Java for code generation, avoiding the potential C++ "code bloating", and the Java "over casting". (You agree experts ?)
Is this better? Let's just say different.
.NET generics had been created to achieve two goals :
-
Improving the performance by avoiding the boxing and unboxing encountered when dealing with value types and "generic solution" (meaning here standard solutions, being applicable for many types, and so dealing with objects)
-
Ensure type safety
In my view, Microsoft as stated that they could deal with these two goals and had to face a choice that will anyway decrease a bit the performance:
The final choice was:
-
We'll generate one new class / structure / method / ... for each ValueType using it
-
We'll generate only one class / structure / method / ... dealing with objects. A "compiler trick" can ensure type safety and give the illusion we work with strong typed solutions and doing some casts when appropriate
How can we verify that ? "Simply" by combining the method hiding and the generics (remember method hiding works with static types !)
Generics and method hiding
To avoid dealing with generic constraints (which won't anything by the way), let's create a simple method that will hide the ToString method and a simple generic method that will call the ToString().
public class C
{
public new virtual string ToString()
{
return "In Class C";
}
}
public class Generic
{
public static void DisplayToString<T>(T t)
{
Console.WriteLine(t.ToString());
}
}
This case is quite simple. So now let's compare what happens if we call the ToString method directly or via generics :
C c = new C();
object o = new C();
Console.WriteLine(c.ToString());
Console.WriteLine(o.ToString());
Generic.DisplayToString(c);
Generic.DisplayToString(o);
Generic.DisplayToString<C>((C)o);
Why such a difference ? Simple, in fact when using the generic method, you are using one and only one method that takes an "object" and so the static type is "object".
Surprising ? Well I would say interesting :-) And definitely something to know if we are using some "new" keywords in our code. Maybe it would be another argument to avoid it ?