oct. 10

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();           //Display "In A"
a.DoSomethingVirtual();    //Display "In A" 
 
B b = new B();
b.DoSomething();           //Display "In B"
b.DoSomethingVirtual();    //Display "In B" 
 
A a1 = new B();
a1.DoSomething();          //Display "In A"
a1.DoSomethingVirtual();   //Display "In B" 

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:

  • either by generated a "huge" DLL (the typical code bloating due to some C++ compilers)
  • or by doing some casts

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());    //Display "In Class C"
Console.WriteLine(o.ToString());    //Display "Namespace.C" 
 
Generic.DisplayToString(c);         //Display "Namespace.C"
Generic.DisplayToString(o);         //Display "Namespace.C"
Generic.DisplayToString<C>((C)o);   //Display "Namespace.C"

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 ?

Tags: | |

Commentaires

Didier Pieroux

Posted on lundi, 15 octobre 2007 11:58

>> 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 ?)

C++
---
If by code bloating, you mean that for each instantiation with a different generic (template) (set of) parameter(s), C++ generates a full binary code, yes, that's correct.

But why are the C++ guys so stupid? Well, they are not of course. Remember that a major difference between C++ and Java/C# is that C++ does not enforce the use of references (i.e. a fix length "pointer" to the object). For the best and the worst. But this is a key feature to set-up a solution of generic as proposed by Java/C#.

However, a design pattern of C++ is that you should split your template class in two parts: one that is not generic and that contains most of the code, and one that contains generic code. You can then end up with a result very similar to what you have in C#.

For instance, in C++ you can define an empty Object class, write the algorithms for references of Object instances, and then use generics for the casting issues. Does this looks familiar to you? ;) Funny enough, because of the existence of the Object class in C#/Java, this principle has been cleverly pushed forward as a language feature.

C++ again: to be fair, we should also mention that the template mechanism offered by C++ allows for "meta-programming", which neither C#/Java (nor Ada by the way) allows. It is a somewhat advanced feature though, and probably miles out away of the average developer.

Java
----
What do you mean by "over casting"? In java, once you use the full generic model, you don't need to cast anymore.

>> 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

That's also exactly the Java way too. For the record, the JVM was not impacted by the introduction of the generics, only the compiler was. This is really in line with what you call “a compiler trick”. For me, the only fundamental difference between the C# way and the Java one is in the treatment of the valueType. Java uses autoboxing instead of generating specific code (less binary code, but slight performance penalty). But for class instances, I believe that the solutions are fundamentally the same, only details seem different to me (like the nice possibility to invoke a constructor for a generic parameter in C#, which is not possible in Java).

Pierre-Emmanuel Dautreppe

Posted on lundi, 15 octobre 2007 12:34

Smile Always there where I expect you to be ! Wink

Interesting comment ! What I was meaning by "over casting" is exactly what you say. I admit this sentence was far from being well-chosen Smile
For Java, there is no difference between value types and reference types for generics and yes, this results in boxing. That's what I was meaning.

But by the way, do Java guys often uses the value type "int" instead of the reference type "Integer" ? If not, this difference is not a big one.

Didier Pieroux

Posted on lundi, 15 octobre 2007 13:13

The common usage is to use int. However, as far as generic are used, this is not an issue in practice. Declare the generic with Integer, but then use "int" values instead if you wish.

Note: just to be sure that we are talking of the same thing... since version 1.5, the boxing/unboxing is implicit in java. That's what 'auto' means in autoboxing. We don't have to do it ourself, so in practice it is completely transpartent.

For instance the following code is correct:
List<Integer> l = new ArrayList<Integer>();
l.add(3);         // Autoboxing from (int)3 => (Integer)3
int a = l.get(0); // Auto-unboxing from (Integer)3 => (int)3


Pierre-Emmanuel Dautreppe

Posted on lundi, 15 octobre 2007 14:19

This example makes me think of something Wink

Yes, it works in the same way in .NET. Boxing and unboxing are automatic and represent the process to move "something" from the heap to the stack and vice and versa.
This is transparent but cause some loss in performances. But I have never checked how much was the loss.

Well to be precise, boxing is automatic : you can write
  int i = 10;
  object o = i; //auto-boxing
But you will need to cast
  int j = (int)o; // forcing the unboxing
as we don't have like in Java two "object" representing a value type.

Metabolic Cooking

Posted on samedi, 21 septembre 2013 18:38

The generalizations during this movie are then prevalent Showmanship belief of Italian Americanism, but if you ask me there are additional positive truth listed here today. Signs or symptoms might be formulated of a selection of items. This price-additional services investigations with the consistent by using vocabulary, the appropriate info of sources and maintenance of the ideal style designs.

Spanish have grown to be progressively noticeable  Metabolic Cooking - http://www.yourshortlink.net/metabolic-cooking

Ajouter un commentaire




biuquote
  • Commentaire
  • Aperçu immédiat
Loading