C# versus Java

Introduction

Combien de fois n'a t-on pas entendu les questions ou réflexions suivantes : "mais qu'est-ce que ça fait de plus que Java, ce C# ? " ou encore "C# ? Ah bah oui, c'est le Java propriétaire de Microsoft pour contrer Sun" et combien de fois n'avez vous pas eu envie d'expliquer ou de convaincre à ces personnes, au demeurant de bonne foi, que C# méritait plus d'estime car il possédait de multiples facettes et qualités souvent trop longues à énumérer ou à résumer au détour d'un forum de discussions. D'ailleurs, dans la majeure partie des cas, ce dédain face à un nouveau langage, qui plus est développé par Microsoft (quel sacrilège ;-)), n'est qu'un moyen de dissimuler une certaine forme d'ignorance.

Cet article intitulé "C# versus Java" se propose de vous faire connaître C# à travers un ensemble de caractéristiques communes ou différentes de Java (Exceptions, Héritages, ...) avec un regard serein et objectif. Devant la multitude de sujets proposés, nous avons essayé de regrouper les fonctionnalités qui présentaient des similitudes, celles qui différaient de part leur implémentation et enfin, celles qui avaient un caractère novateur.

La plupart des exemples ont été testés avec  Microsoft .NET Framework beta 2 et Java 2 Standard Edition.

L'auteur de l'article original est Dare Obasanjo qui a eu la gentillesse de nous autoriser à enrichir et à traduire ses écrits. Certaines parties ont été totalement revisitées par nos soins pour plus de clarté et de simplicité mais en aucun cas le sens et la teneur de l'article original n'ont été modifiés. Nous espérons qu'après avoir lu ces quelques pages vous serez plus à même de comprendre ce qu'est C#, mais aussi d'analyser ou de mieux juger par vous même ses multiples caractéristiques et fonctionnalités.  

 

Index Rapide

  1. Plus les choses évoluent plus elles tendent à se ressembler
    Cette section décrit les concepts et les caractéristiques très proches de C# et Java
    1. Nous sommes tous des objets
    2. Synthèse des mots-clés
    3. A propos des machines virtuelles
    4. Gestion de la mémoire (ramasse miettes)
    5. Les tableaux
    6. Pas de méthodes globales
    7. Les interfaces, Oui. L'héritage multiple, Non.
    8. Les chaînes de caractères ne peuvent être modifiées
    9. Les classes non extensibles
    10. Levée et Capture d'Exceptions
    11. Initialisation de membres et constructeurs statiques
  2. La même chose mais en différent
    Cette section décrit les concepts et caractéristiques des langages qui diffèrent soit au niveau de la syntaxe soit dans la façon d'être implémentés.
    1. La méthode Main
    2. La syntaxe de l'héritage  
    3. L'opérateur d'identification de type  (is operator)
    4. Les Namespaces ou packages
    5. Constructeurs, Destructeurs et Finaliseurs
    6. Synchronisation de méthodes
    7. Accessibilité à la classe
    8. La Reflection
    9. Déclaration de constantes
    10. Les types primitifs
    11. La déclaration de tableaux
    12. Chaînage et appel du constructeur père
  3. Comme un air de "Déjà Vu
    Cette section décrit les concepts et les caractéristiques de C# existants dans JAVA mais avec une importante différence au niveau de leur implémentation.
    1. Les classes imbriquées
    2. Les threads et les membres volatiles
    3. La surcharge d'opérateurs
    4. L'instruction "switch"
    5. Les Assemblies
    6. Collections
    7. Goto (plus considéré comme dangereux)
    8. Les méthodes virtuelles (et finales)
    9. Fichiers Entrée/Sortie
    10. La sérialisation
    11. La génération de la documentation à partir du code source
    12. Plusieurs classes dans un même fichier
    13. L'import de bibliothèques
    14. Les évènements
    15. Intéropérabilité inter-langages
  4. Maintenant, des choses totalement différentes
    Cette section décrit des fonctionnalités existantes dans C# n'ayant aucun équivalent en Java.
    1. La libération déterministe d'objets
    2. L'ordre Delegate
    3. Enumérations
    4. Les types valeur
    5. Le Boxing et Unboxing
    6. Identification de type dynamique (operator "as")
    7. L'instruction "for each"
    8. Les propriétés
    9. Les attributs
    10. Les indexeurs
    11. Les directives de pré-processing
    12. Les aliases
    13. Génération de code dynamique
    14. Pointeur et code non protégé(unsafe)
    15. Passage par référence
    16. Les listes variables de paramètres
    17. Les caractères spéciaux
    18. Détection de débordement
    19. Implémentation explicite d'interface
  5. Concepts avancés
    Cette section décrit les concepts avancés de C# et Java
    1. La portabilité multi-plateformes (Write Once, Run Anywhere)
    2. Les extensions
    3. Le chargement dynamique de classe
    4. Les interfaces contenant des champs
    5. Les classes anonymes
    6. Le versionning des Assemblies
  1. Conclusion
  2. Ressources
  3. Remerciements

q       Plus les choses évoluent plus elles tendent à se ressembler

1.     Nous sommes tous des objets !

Comme Java, C# possède une super classe, mère de tous les objets : System.Object. La classe équivalent s'appelle java.lang.object. Les méthodes présentent dans ces deux classes sont très similaires (ex: toString()) excepté wait(), notify() et les autres méthodes liées à la synchronisation.

NOTE: En C#, une classe de type objet peut soit être écrite sous la forme "object" en minuscule ou "Object". En fait, à la compilation ces deux types sont remplacés par System.Object.

2.     Synthèse des mots-clés

Il y a énormément de similitudes entre les deux langages, presque tous les mots-clés Java ont un équivalent en C# à part quelques exceptions telles que transient, throws et strictfp. La table ci-dessous vous illustre les mots-clés présents des deux cotés

mot-clé C#

mot-clé Java

mot-clé C#

mot-clé Java

mot-clé C#

mot-clé Java

mot-clé C#

mot-clé Java

abstract

abstract

explicit

N/A

object

N/A

this

this

as

N/A

extern

native

operator

N/A

throw

throw

base

super

finally

finally

out

N/A

true

true

bool

boolean

fixed

N/A

override

N/A

try

try

break

break

float

float

params

N/A

typeof

N/A

byte

N/A

for

for

private

private

uint

N/A

case

case

foreach

N/A

protected

N/A

ulong

N/A

catch

catch

get

N/A

public

public

unchecked

N/A

char

char

goto

goto1

readonly

N/A

unsafe

N/A

checked

N/A

if

if

ref

N/A

ushort

N/A

class

class

implicit

N/A

return

return

using

import

const

const1

in

N/A

sbyte

byte

value

N/A

continue

continue

int

int

sealed

final

virtual

N/A

decimal

N/A

interface

interface

set

N/A

void

void

default

default

internal

protected

short

short

while

while

delegate

N/A

is

instanceof

sizeof

N/A

:

extends

do

do

lock

synchronized

stackalloc

N/A

:

implements

double

double

long

long

static

static

N/A

strictfp

else

else

namespace

package

string

N/A

N/A

throws

enum

N/A

new

new

struct

N/A

N/A

transient

event

N/A

null

null

switch

switch

N/A

volatile

3.     A propos des machines virtuelles

De la même manière que Java est compilé en byte-code et s'exécute dans un environnement d'exécution managé (Machine Virtuelle JVM), C# est compilé en MSIL s'exécutant dans la CRL (Common Langage Runtime). Les deux plate-formes supportent la compilation Just In Time (JIT). Toutefois, il existe une légère différence entre les deux plate-formes, les compilateurs Java permettent de désactiver totalement le JIT en fonctionnant uniquement en mode interprété alors que les compilateurs .NET en général intègre nativement le JIT. Enfin, il existe des deux cotés la possibilité de pré-compiler en code natif le source. .

4.     Gestion de la mémoire (ramasse miettes)

Tous les objets Java sont créés sur le tas en utilisant le mot-clé new. Il en va de même pour la plupart des objets C# n'étant pas des types de valeurs (ValueType).

Le ramasse miettes en C# implémente un algorithme bien connu appelé Mark and Compact garbage collection algorithm.

5.     Les tableaux

Dans les langages tels que C ou C++, la dimension de chaque sous tableau doit être identique dans les tableaux à plusieurs dimensions. En Java et C#, les tableaux n'ont pas à se plier à cette contrainte car ils peuvent être créés comme des tableaux à une dimension référençant d'autres tableaux. Ils sont appelés "Jagged Arrays". Pour cette raison, les tailles initiales des lignes et colonnes de ce type de tableaux peuvent-être différentes. C'est ce que montre le code suivant :

int [][]myArray = new int[2][];

myArray[0] = new int[3];

myArray[1] = new int[9];

 

Le code précédent est valide pour Java et C#.

6.     Pas de méthodes globales

Comme en Java et contrairement à C++, les méthodes en C# doivent être intégrées dans une classe.

7.     Les Interfaces, Oui. L'héritage multiple, Non

C#, comme Java, supporte le concept d'interface qui est assimilé à une classe abstraite pure. De la même façon, C# et Java autorisent l'héritage multiple d'interface et simple d'implémentation.

8.     Les chaînes de caractères ne peuvent être modifiées

C# possède une classe System.String qui est équivalente à java.lang.String. Les deux classes sont "immuables", c'est à dire qu'une fois les objets créés, il n'est pas possible de modifier leur valeur. Ceci dans le but de protéger le mécanisme d'encapsulation ô combien fondamental.

C# Code

string csString = "Apple Jack";

csString.ToLower(); /* Does not modify string, instead returns lower case copy of string */

 
Java Code

String jString = "Grapes";

jString.toLowerCase(); /* Does not modify string, instead returns lower case copy of string */

 

Pour créer une chaîne de caractères autorisant la modification avec la même référence, il est conseillé d'utiliser  les classes System.Text.StringBuilder pour C# et java.lang.StringBuffer pour Java.


NOTE: En C#, la classe chaîne peut être écrite sous la forme string ou String.

9.     Les classes non extensibles

Les deux langages proposent des mécanismes consistant à interdire toute extension d'une classe. Soit par souci d'optimisation, soit par souci de sécurité. Ainsi, il est interdit de les dériver pour redéfinir des méthodes ou simplement réutiliser l'implémentation. En C#, vous utilisez le mot-clé sealed  et en Java le mot-clé final.

C# Code

sealed class Student

{

         string fname;

         string lname;

         int uid;

         void attendClass() {}

}

 

Java Code

final class Student

{

    String fname;

    String lname;

    int uid;

    void attendClass() {}

}

 

10.                       Levée et Capture d'Exceptions

Les exceptions en C# et Java partagent énormément de caractéristiques. Les deux langages supportent l'utilisation de l'ordre try pour indiquer qu'un bloc est susceptible de lever une exception et catch pour capturer l'exception en question. De plus, finally est implémenté de la même manière pour spécifier qu'une région de code doit, dans tous les cas être exécutée (exception ou pas). Cela permet de libérer des ressources proprement. Les deux langages proposent une hiérarchie de classes d'Exceptions dérivant d'une super classe : System.Exception pour C# et java.lang.Exception pour Java. Aussi, il est possible de chaîner la levée ou la capture d'exception (throw dans un catch) de part et d'autre. Cela permet, lors de la levée d'une exception, de retourner à l'appelant un type d'exception correspondant à son contexte et à sa couche d'architecture. Par exemple, une ligne non trouvée dans une table se traduira par une SQLException que le développeur prendra soin de renvoyer à l'interface graphique sous la forme d'un ObjectNotFoundException.

NOTE: Cependant, il existe une différence fondamentale entre C# et Java. Le mot-clé throws n'existe pas en C# car vous n'êtes pas contraint de spécifier dans la signature d'une méthode le fait qu'elle est susceptible de lever une exception. Il n'y a, contrairement à Java, aucune vérification de faite à l'exécution.

C# Code

using System;

using System.IO;

 

class MyException: Exception

{

  public MyException(string message): base(message){ }

  public MyException(string message, Exception innerException):

         base(message, innerException){ }

}

 

public class ExceptionTest

{

  static void DoStuff()

  { 

         throw new FileNotFoundException(); 

  }

  public static int Main()

  {

         try

         {           

                 try

                 {       

                         DoStuff();

                         return 0;  //won't get to execute

                 }

                 catch(IOException ioe)

                 { /* parent of FileNotFoundException */

                         throw new MyException("MyException occured", ioe);
                    /* rethrow new exception with inner exception specified */

                 }

         }

         finally

         {

                 Console.WriteLine("***Finally block executes even though MyException not caught***");

         }

  }//Main(string[])

} // ExceptionTest

 
Java Code

class MyException extends Exception{

    public MyException(String message){ super(message); }

    public MyException(String message, Exception innerException){ super(message, innerException); }

}

 

public class ExceptionTest {

    static void doStuff(){ 

    throw new ArithmeticException();   

    }

 

    public static void main(String[] args) throws Exception{

    try{           

        try{       

        doStuff();

        return;  //won't get to execute

        }catch(RuntimeException re){ /* parent of ArithmeticException */

        throw new MyException("MyException occured", re); /* rethrow new exception with cause specified */

        }

    }finally{

        System.out.println("***Finally block executes even though MyException not caught***");

    }

}//main(string[])

} // ExceptionTest

 

11.                       Initialisation de membres et constructeurs statiques

Les variables d'instance et les variables statiques peuvent être initialisées dès leur déclaration, et ce, dans les deux langages. Si la variable membre est une variable d'instance, alors l'initialisation sera effectuée juste avant l'appel du constructeur. Les membres statiques, eux,  sont initialisés dès le chargement de la classe par le Runtime. Cela intervient en règle générale avant l'appel du constructeur. Il est aussi possible de définir de part et d'autre des blocs statiques qui seront exécutés au chargement. Il sont appelés plus communément constructeurs statiques.

C# Code

using System;

 

class StaticInitTest

{

  string instMember = InitInstance();

  string staMember = InitStatic();

 

  StaticInitTest()

  {

         Console.WriteLine("In instance constructor");

  }

 

  static StaticInitTest()

  {

         Console.WriteLine("In static constructor");

  }

 

  static String InitInstance()

  {

         Console.WriteLine("Initializing instance variable");

         return "instance";

  }

 

  static String InitStatic()

  {

         Console.WriteLine("Initializing static variable");

         return "static";

  }

 

  static void DoStuff()

  {

         Console.WriteLine("Invoking static DoStuff() method");

  }

 

  public static void Main(string[] args)

  {

         Console.WriteLine("Beginning main()");

         StaticInitTest.DoStuff();

         StaticInitTest sti = new StaticInitTest();

         Console.WriteLine("Completed main()");

  }

  }
 
Java Code

class StaticInitTest

{

         String instMember = initInstance();

         String staMember = initStatic();

         StaticInitTest()

         {

                 System.out.println("In instance constructor");

         }

         static

         {

                 System.out.println("In static constructor");

         }

    static String initInstance()

    {

           System.out.println("Initializing instance variable");

   return "instance";

    }

    static String initStatic()

    {

           System.out.println("Initializing static variable");

           return "static";

    }

    static void doStuff()

    {

           System.out.println("Invoking static DoStuff() method");

    }

    public static void main(String[] args)

    {

           System.out.println("Beginning main()");

           StaticInitTest.doStuff();

           StaticInitTest sti = new StaticInitTest();

           System.out.println("Completed main()");

    }

}
 
Exécution des deux exemples : 
In static constructor
Beginning main()
Invoking static DoStuff() method
Initializing instance variable
Initializing static variable
In instance constructor
Completed main()
 

q       La même chose mais en différent

1.     La méthode Main

Le point d'entrée dans les deux langages est la méthode Main(). Il existe une différence mineure concernant le nom de la méthode qui commence par un "M" majuscule et les paramètres utilisés. Ainsi, le main de C# est surchargé et retourne un code de status alors que celui de Java impose une signature bien précise et retourne void.

C# Code

using System;

 

class A

{

  public static void Main(String[] args)

  {

         Console.WriteLine("Hello World");

  }

}

 
Java Code

class B

{

  public static void main(String[] args)

  {

         System.out.println("Hello World");

  }

}

Il est généralement recommandé d'insérer dans chaque classe une méthode Main() permettant tester la classe de manière unitaire indépendamment de la méthode Main() générale.

Ainsi, il est possible d'avoir deux classes, A et B, contenant toutes deux une méthode Main(). En Java, pour spécifier la méthode Main appelée, il suffit de spécifier le nom de la classe en ligne de commande ou à travers un outil. En C#, le problème est légèrement différent car le processus de compilation génère un exécutable pouvant contenir plusieurs Main(). C'est pourquoi, un paramètre de compilation précisera la méthode par défaut qui sera appelée lors de l'exécution (/main). L'utilisation de ces techniques associées à une compilation conditionnelle via les directives de pré-processing vous procure une grande souplesse dans les tests.

Java Exemple
C:\CodeSample> javac A.java B.java 
 
C:\CodeSample> java A
 Hello World from class A
 
C:\CodeSample> java B
 Hello World from class B
 
C# Exemple
C:\CodeSample> csc /main:A /out:example.exe A.cs B.cs 
 
C:\CodeSample> example.exe
 Hello World from class A
 
C:\CodeSample> csc /main:B /out:example.exe A.cs B.cs 
 
C:\CodeSample> example.exe
 Hello World from class B
 

Ainsi, en Java, contrairement à C#, il n'est pas nécessaire de recompiler les classes pour changer le point d'entrée du programme. D'un autre coté, en C#, les directives de compilation permettent de produire du code paramétrable pour la phase de test ou de release.

2.     La syntaxe de l'héritage

C# utilise la syntaxe du C++ pour l'héritage. Le même mot-clé s'utilise pour l'héritage d'implémentation et d'interface contrairement à Java qui spécifie extends et implements.

C# Code

using System;

 

class B:A, IComparable

{

         int CompareTo(){}

  public static void Main(String[] args)

  {

         Console.WriteLine("Hello World");

  }

}

 
Java Code

class B extends A implements Comparable

{

int compareTo(){}

  public static void main(String[] args)

  {

         System.out.println("Hello World");

  }

}

Les développeurs Java pourront toujours argumenter que cela rend les sources difficilement lisibles car le mot-clé indique si la classe mère est une interface ou pas. Dans la pratique, ce n'est pas vraiment un problème dans la mesure où Microsoft définit clairement que les noms d'interfaces doivent commencer par IMyInterface (tel que ICloneable).

3.     L'opérateur d'indentification de type (is operator)

En C#, l'opérateur is est totalement analogue au mot-clé instanceof de Java. Les deux bouts de code suivant sont équivalent :

C# Code

if(x is MyClass)

MyClass mc = (MyClass) x;

 
Java Code

if(x instanceof MyClass)

MyClass mc = (MyClass) x;

 

4.     Les Namespaces ou packages

Un Namespace C# est une manière de regrouper des classes par domaine et s'utilise de manière similaire par rapport au mot-clé package en Java. Les développeurs C++ noteront que la syntaxe des namespaces en C# est très proche de C++. Toutefois, en Java, la structure arborescente des packages dicte la structure des sources sous-jacentes, ce qui n'est pas le cas de C#.

Examples :

C# Code

namespace com.carnage4life

{

  public class MyClass

  {

         int x;

         void doStuff(){}

  }

}

 
Java Code

package com.carnage4life;

 

public class MyClass

{

  int x;

  void doStuff(){}

}

 

Enfin, la syntaxe C#  autorise la multiplicité des déclarations de namespaces au sein d'un même fichier.

C# Code

using System;

 

namespace Company

{

  public class MyClass

  { /* Company.MyClass */

         int x;

         void doStuff(){}

  }

 

  namespace Carnage4life

  {

         public class MyOtherClass

         {  /* Company.Carnage4life.MyOtherClass */

                 int y;

                 void doOtherStuff(){}

                 public static void Main(string[] args)

                 {

                         Console.WriteLine("Hey, I can nest namespaces");

                 }

         }// class MyOtherClass

  }// namespace Carnage4life

}// namespace Company

 

 

5.     Constructeurs, Destructeurs et Finaliseurs

La syntaxe et la sémantique des constructeurs en C# est identique à celle de Java. C# propose aussi le concept de destructeur dans le même esprit que C++ (avec un ~) à ceci près que la sémantique est strictement la même qu'un finaliseur (Finalize) Java. Bien que les finaliseurs soient intégrés au langage, il est recommandé de s'en servir avec précaution car il n'existe aucun contrôle quant à l'ordre dans lequel ils sont appelés. De plus, les objets possédant des finaliseurs ralentissent le traitement du Ramasse miettes qui est contraint de les mémoriser plus longtemps dans le mécanisme de libération.

NOTE: En C#, les destructeurs (finalizers) appellent automatiquement les finaliseurs de leur classe mère contrairement à Java qui impose un appel explicite (super.finalise()).

C# Code

using System;

 

public class MyClass

{

  static int num_created = 0;

  int i = 0;   

 

  MyClass()

  { 

         i = ++num_created;

         Console.WriteLine("Created object #" + i);   

  }

 

  ~MyClass()

  {

         Console.WriteLine("Object #" + i + " is being finalized"); 

  }

 

  public static void Main(string[] args)

  {

    for(int i=0; i < 10000; i++)

                 new MyClass();

         }

}

 
Java Code

public class MyClass

{

  static int num_created = 0;

  int i = 0;   

 

  MyClass()

  { 

         i = ++num_created;

         System.out.println("Created object #" + i);   

  }

 

  public void finalize()

  {

         System.out.println("Object #" + i + " is being finalized"); 

  }

 

  public static void main(String[] args)

  {

         for(int i=0; i < 10000; i++)

                 new MyClass();

  }

}

 

6.     Synchronisation de méthodes

En Java, il est possible de spécifier explicitement la synchronisation d'un bloc afin d'éviter que deux threads modifient simultanément la même section critique. C# fournit un mot-clé lock qui est sémantiquement l'équivalent du synchronized en Java.

C# Code

public void WithdrawAmount(int num)

{

lock(this)

{

if(num < this.amount)

this.amount -= num;

}

}

 
Java Code

public void withdrawAmount(int num)

{

synchronized(this)

{

if(num < this.amount)

this.amount -= num;

}

}
 

C# et Java supportent tous les deux les méthodes synchronisées. Lorsqu'une méthode synchronisée est appelée, le premier thread prend la main sur le moniteur d'objets et bloque l'accès aux autres threads le temps de l'exécution de la méthode. En Java, les méthodes synchronisées sont précédées du mot-clé synchronized. En C#, il existe plusieurs manières d'implémenter la synchronisation de méthodes : par Attributs de Runtime (MethodImplOptions.Synchronized) ou par utilisation du mot-clé Interlocked.

C# Code

using System;

using System.Runtime.CompilerServices;

 

public class BankAccount

{

  [MethodImpl(MethodImplOptions.Synchronized)]

  public void WithdrawAmount(int num)

  {

         if(num < this.amount)

                 this.amount - num;

  }

}//BankAccount
 
Java Code

public class BankAccount

{

  public synchronized void withdrawAmount(int num)

  {

         if(num < this.amount)

         this.amount - num;

  }

}//BankAccount

7.     Accessibilité à la classe

La table ci-dessous vous synthétise les différents mot-clés permettant de modifier la visibilité et l'accès à une classe dans le but de protéger l'encapsulation. Les fans de C++ déçus lorsque Sun a modifié la portée de l'instruction protected seront heureux de noter que C# garde la même sémantique que C++. Cela signifie donc qu'un membre "protected" ne peut être accédé que par d'autres méthodes membres situées dans la même classe ou dans une classe dérivée mais n'est en aucun cas visible de l'extérieur (même package ou non).

Le mot-clé internal signifie que la fonction membre ne peut être accédée que par d'autres classes situées dans la même assembly. Associée à protected, la visibilité est étendue aux classes dérivées.

C# access modifier

Java access modifier

private

private

public

public

internal

protected

protected

N/A

internal protected

N/A

NOTE: La visibilité par défaut d'un champ ou d'une méthode en C# est private alors qu'en Java elle est  protected.

8.     La Reflection

La capacité à découvrir les méthodes et champs dans une classe et à invoquer dynamiquement des méthodes à l'exécution est appelé la reflection. Cette caractéristique existe aussi bien en Java qu'en C# à la différence près que la reflection s'effectue au niveau d'une assembly en C# et au niveau d'une classe en Java.

C# Code

using System;

using System.Xml;

using System.Reflection;

using System.IO;

 

class ReflectionSample

{

  public static void Main( string[] args)

  {

         Assembly assembly=null;

         Type type=null;

         XmlDocument doc=null;

         try

         {

                 // Load the requested assembly and get the requested type

                 assembly = Assembly.LoadFrom("C:\\WINNT\\Microsoft.NET\\Framework\\v1.0.2914\\System.XML.dll");

                 type = assembly.GetType("System.Xml.XmlDocument", true);

 

                 //Unfortunately one cannot dynamically instantiate types via the Type object in C#.

                 doc  =  Activator.CreateInstance("System.Xml","System.Xml.XmlDocument").Unwrap() as XmlDocument;

                 if(doc != null)

                         Console.WriteLine(doc.GetType() + " was created at runtime");

                 else

                         Console.WriteLine("Could not dynamically create object at runtime");

         }

         catch(FileNotFoundException)

         {

                 Console.WriteLine("Could not load Assembly: system.xml.dll");

                 return;

         }

         catch(TypeLoadException)

         {

                 Console.WriteLine("Could not load Type: System.Xml.XmlDocument from assembly: system.xml.dll");

                 return;

         }

         catch(MissingMethodException)

         {

                 Console.WriteLine("Cannot find default constructor of " + type);

         }

         catch(MemberAccessException)

         {

                 Console.WriteLine("Could not create new XmlDocument instance");

         }

         // Get the methods from the type

         MethodInfo[] methods = type.GetMethods();

         //print the method signatures and parameters

         for(int i=0; i < methods.Length; i++)

         {

                 Console.WriteLine ("{0}", methods[i]);

                 ParameterInfo[] parameters = methods[i].GetParameters();

                 for(int j=0; j < parameters.Length; j++)

                 {  

                         Console.WriteLine (" Parameter: {0} {1}", parameters[j].ParameterType, parameters[j].Name);

                 }

         }//for (int i...)

  }

}

 
Java Code

import java.lang.reflect.*;

import org.w3c.dom.*;

import javax.xml.parsers.*;

 

class ReflectionTest

{

  public static void main(String[] args)

  {

         Class c=null;

         Document d;

         try

         {

                 c =  DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument().getClass();

                 d =  (Document) c.newInstance();

                 System.out.println(d + " was created at runtime from its Class object");

         }

         catch(ParserConfigurationException pce)

         {

                 System.out.println("No document builder exists that can satisfy the requested configuration");

         }

         catch(InstantiationException ie)

         {

                 System.out.println("Could not create new Document instance");

         }

         catch(IllegalAccessException iae)

         {

                 System.out.println("Cannot access default constructor of " + c);

         }

         // Get the methods from the class

         Method[] methods = c.getMethods();

         //print the method signatures and parameters

         for (int i = 0; i < methods.length; i++)

         {

                 System.out.println( methods[i]);

                 Class[] parameters = methods[i].getParameterTypes();

                 for (int j = 0; j < parameters.length; j++)

                 {        

                         System.out.println("Parameters: " +  parameters[j].getName());

                 }

         }

  }

}

A la vue du code précédent, vous aurez remarqué que la granularité de la reflection est plus grande en C# qu'en Java. Quelque fois, vous avez besoin d'obtenir les méta-données d'une classe spécifique encapsulée sous la forme d'un objet. Cet objet est du type java.lang.Class en Java et System.Type en C#. Ainsi, pour récupérer une instance de cette classe, vous utilisez getClass() en Java et GetType() en C#.

C# Code
Type t = typeof(ArrayList);
 
Java Code
Class c = java.util.Arraylist.class; /* Must append ".class" to fullname of class */
 

9.     Déclaration de constantes

Pour déclarer des constantes en Java, vous devez spécifier le mot-clé final. Les variables "finales" peuvent être initialisées soit à la compilation, soit à l'exécution. Une fois la variable initialisée, elle devient immuable (non modifiable). Lorsque cette variable est une référence, la référence ne pourra pointer que sur un seul objet durant sa durée de vie. Pour déclarer des constantes en C#, le mot-clé const est utilisé pour la compilation et readonly à l'exécution. const a toujours été demandé par la communauté de développeurs Java mais n'a jamais vraiment eu d'écho de la part de Sun. Gageons que la prochaine version du JDK intègrera ce mot-clé au langage car, à l'heure actuelle, le développeur est contraint de jongler avec le clonage d'objets et le masquage par interface afin d'éviter de briser l'encapsulation.

Contrairement à C++, il n'est possible ni en Java ni en C# de spécifier une classe immuable (équivalent du const MyClass* p).

C# Code

using System;

 

public class ConstantTest

{

         /* Compile time constants */

  const int i1 = 10;   //implicitly a static variable

         // code below won't compile because of 'static' keyword

  // public static const int i2 = 20;

  /* run time constants */

public static readonly uint l1 =  (uint) DateTime.Now.Ticks;

         /* object reference as constant */

  readonly Object o = new Object();

  /* uninitialized readonly variable */

  readonly float f;

  ConstantTest()

  {

         // unitialized readonly variable must be initialized in constructor

         f = 17.21f;

  }

}

 
Java Code

import java.util.*;

 

public class ConstantTest

{

  /* Compile time constants */

  final int i1 = 10;   //instance variable

  static final int i2 = 20; //class variable

  /* run time constants */

  public static final long l1 = new Date().getTime();

  /* object reference as constant */

  final Vector v = new Vector();

  /* uninitialized final */

  final float f;

  ConstantTest()

  {

         // unitialized final variable must be initialized in constructor

         f = 17.21f;

  }

}

NOTE : Le langage Java supporte aussi les paramètres de méthodes précédés du mot-clé final. Cela permet aux classes internes déclarées dans la méthode d'accéder aux paramètres en question. Cette fonctionnalité n'existe pas en C# .

10.                       Les types primitifs

Pour chaque type primitif en Java, il existe un correspondant en C# avec le même nom, excepté byte. Le byte  en Java est signé et est ainsi proche du type sbyte  de C# (et non byte). De plus, C# possède des versions non signées pour la plupart des types : ulong, uint, ushort et byte. La seule différence majeure provient du type decimal qui n'effectue aucun arrondi au prix d'un surplus de place et d'une rapidité de traitement moindre.

Ci-dessous, plusieurs manières d'implémenter des valeurs réelles en C#.

C# Code

decimal dec = 100.44m; //m is the suffix used to specify decimal numbers

double  dbl = 1.44e2d; //e is used to specify exponential notation while d is the suffix used for doubles

 

11.                       La déclaration de tableaux

Java possède deux façons de déclararer un tableau : la première, créé pour la compatibilité avec C et C++ et la deuxième plus simple et plus claire. C# utilise, quant à lui, la deuxième façon.

C# Code

int[] iArray = new int[100]; //valid, iArray is an object of type int[]

float fArray[] = new float[100]; //ERROR: Won't compile 

 
Java Code

int[] iArray = new int[100]; //valid, iArray is an object of type int[]

float fArray[] = new float[100]; //valid, but isn't clear that fArray is an object of type float[]

 

12.                       Chaînage et appel du constructeur père

C# et Java appelle implicitement les constructeurs père en cas de création d'un objet. Les deux langages proposent un moyen d'appeler explicitement le constructeur père en lui passant des paramètres spécifiques. De plus, C# et Java assurent que l'appel des constructeurs se fait dans un ordre bien précis assurant ainsi l'impossibilité d'accéder à des variables non encore initialisées. Enfin les deux langages intègrent la surcharge de constructeur et l'appel inter-constructeur afin de réutiliser du code existant. Ce procédé est aussi appelé "chaînage de constructeurs".

C# Code

using System;

 

class MyException: Exception

{

  private int Id;

  public MyException(string message): this(message, null, 100){ }

  public MyException(string message, Exception innerException):

         this(message, innerException, 100){ }

  public MyException(string message, Exception innerException, int id):

         base(message, innerException)

  {

         this.Id = id;  

  }

}

  
Java Code

class MyException extends Exception

{

private int Id;

         public MyException(String message)

  {

                 this(message, null, 100);

  }

  public MyException(String message, Exception innerException)

  {

         this(message, innerException, 100);

}

  public MyException( String message,Exception innerException, int id)

  {

         super(message, innerException);

         this.Id = id;    

  }

}

 

q       Comme un air de "Déja Vu"

1.     Les classes imbriquées

En Java et en C# il est possible d'imbriquer des déclarations de classes. En Java, il existe deux types de classes imbriquées : les classes non statiques connues sous le nom de classe internes, et les classes statiques. Une classe Java interne peut être considérée comme une relation 1,1 avec sa classe englobante et ne peut posséder de méthodes statiques.

C# possède l'équivalent des classes statiques Java imbriquées mais n'intègre rien d'équivalent aux classes internes de Java. Le code suivant nous montre un exemple de classe imbriquée en C# et Java.

C# Code

public class Car

{

  private Engine engine;

  private class Engine

  {

         string make;

  }

}

 
Java Code

public class Car

{

  private Engine engine;

  private static class Engine

  {

         String make;

  }

}

NOTE : En Java, une classe imbriquée peut être déclarée dans n'importe quels types de bloc, ce qui n'est pas le cas de C#. La possibilité de créer des classes imbriquées à l'intérieur de méthodes peut sembler inutile mais combinée aux classes anonymes, cela peut conduire à concevoir de puissantes applications basées sur les Design Pattern.

2.     Les threads et les membres volatiles

Les Threads en Java sont créés par dérivation de la classe java.lang.Thread et en re-définissant la méthode run() ou par implémentation directe de l'interface the java.lang.Runnable. Vous avez le choix entre l'héritage (historique) et la délégation (plus sûre).

En C#, la création de Threads se fait à l'aide de l'opération new System.Threading.Thread en passant en paramètre un délégué (System.Threading.ThreadStart) qui n'est autre qu'un pointeur vers la méthode destinée à être exécutée. Contrairement à Java qui impose comme point d'exécution d'un Tread la fonction run(), il est possible en C# de spécifier n'importe quelle fonction

En Java, toutes les classes héritent des méthodes wait(), notify() and notifyAll() situées dans java.lang.Object. Les méthodes équivalentes en C# sont Wait(), Pulse() and PulseAll() se trouvant dans la classe System.Threading.Monitor.

L'example ci-dessous nous déroule le scénario dans lequel des threads sont lancés dans un ordre bien défini et doivent être traités dans le même ordre. Comme l'exécution des threads est par nature non-déterministe, ceux qui arriveront après les autres devront attendre leur tour à l'aide de la méthode wait().

C# Code

using System;

using System.Threading;

using System.Collections;

 

public class WorkerThread

{

  private int idNumber;

  private static int num_threads_made = 1;

  private ThreadSample owner;

 

  public WorkerThread(ThreadSample owner)

  {

         idNumber = num_threads_made;   

         num_threads_made++;       

         this.owner = owner;

  }/* WorkerThread() */

   

  //sleeps for a random amount of time to simulate working on a task

  public void PerformTask()

  {

         Random r = new Random((int) DateTime.Now.Ticks);

         int timeout = (int) r.Next() % 1000;

         if(timeout < 0)

                 timeout *= -1;

         //Console.WriteLine(idNumber + ":A");

    try

         {       

                 Thread.Sleep(timeout);     

         }

         catch (ThreadInterruptedException e)

         {

                 Console.WriteLine("Thread #" + idNumber + " interrupted");

         }

    //Console.WriteLine(idNumber + ":B");

         owner.workCompleted(this);

  }/* performTask() */

  public int getIDNumber() {return idNumber;}

} // WorkerThread

 

public class ThreadSample

{

  private static Mutex m = new Mutex();

  private ArrayList threadOrderList = new ArrayList();

  private int NextInLine()

  {

         return (int) threadOrderList[0];

  }

  private void RemoveNextInLine()

  {

         threadOrderList.RemoveAt(0);

         //all threads have shown up

         if(threadOrderList.Count == 0)

                 Environment.Exit(0);

  }

  public void workCompleted(WorkerThread worker)

  {

         try

         {

                 lock(this)

                 {

                         while(worker.getIDNumber() != NextInLine())

                         {

                                try

                                {

                            //wait for some other thread to finish working

                                        Console.WriteLine ("Thread #" + worker.getIDNumber() +

" is waiting for Thread #" +

                                                               NextInLine() + " to show up.");

                                        Monitor.Wait(this, Timeout.Infinite);

                                }

                                catch (ThreadInterruptedException e) {}

                         }//while

                         Console.WriteLine("Thread #" + worker.getIDNumber() + " is home free");

                         //remove this ID number from the list of threads yet to be seen

                         RemoveNextInLine();

                         //tell the other threads to resume

                         Monitor.PulseAll(this);

                 }

         }

         catch(SynchronizationLockException){Console.WriteLine("SynchronizationLockException occurred");}

  }

  public static void Main(String[] args)

  {

         ThreadSample ts = new ThreadSample();

         /* Launch 25 threads */

         for(int i=1; i <= 25; i++)

         {

                 WorkerThread wt = new WorkerThread(ts);

                 ts.threadOrderList.Add(i);

                 Thread t = new Thread(new ThreadStart(wt.PerformTask));

                 t.Start();   

         }

         Thread.Sleep(3600000); //wait for it all to end

  }/* main(String[]) */

}//ThreadSample

 
Java Code

import java.util.*;

 

class WorkerThread extends Thread

{

private Integer idNumber;

         private static int num_threads_made = 1;

         private ThreadSample owner;

 

         public WorkerThread(ThreadSample owner)

         {

super("Thread #" + num_threads_made);  

                 idNumber = new Integer(num_threads_made);  

                 num_threads_made++;       

                 this.owner = owner;

                 start(); //calls run and starts the thread.

         }/* WorkerThread() */

         //sleeps for a random amount of time to simulate working on a task

         public void run()

{

Random r = new Random(System.currentTimeMillis());

             int timeout = r.nextInt()  % 1000;

if(timeout < 0)

                 timeout *= -1 ;

                 try{       

                Thread.sleep(timeout);     

        } catch (InterruptedException e){

                 System.out.println("Thread #" + idNumber + " interrupted");

        }

                 owner.workCompleted(this);

         }/* run() */

              

         public Integer getIDNumber() {return idNumber;}

} // WorkerThread

 

public class ThreadSample{

private Vector threadOrderList = new Vector();

         private Integer nextInLine(){

                 return (Integer) threadOrderList.firstElement();

         }

         private void removeNextInLine(){

threadOrderList.removeElementAt(0);

             //all threads have shown up

             if(threadOrderList.isEmpty())

             System.exit(0);

    }

    public synchronized void workCompleted(WorkerThread worker){

             while(worker.getIDNumber().equals(nextInLine())==false)

{

            try {

                         //wait for some other thread to finish working

                         System.out.println (Thread.currentThread().getName() + " is waiting for Thread #" +

                                                       nextInLine() + " to show up.");

                         wait();

                 } catch (InterruptedException e) {}

                 }//while

                 System.out.println("Thread #" + worker.getIDNumber() + " is home free");

                 //remove this ID number from the list of threads yet to be seen

                 removeNextInLine();

                 //tell the other threads to resume

                 notifyAll();

         }

         public static void main(String[] args) throws InterruptedException

{

                 ThreadSample ts = new ThreadSample();

                 /* Launch 25 threads */

                 for(int i=1; i <= 25; i++)

{

                 new WorkerThread(ts);

                 ts.threadOrderList.add(new Integer(i));

                 }

                 Thread.sleep(3600000); //wait for it all to end

         }/* main(String[]) */

}//ThreadSample

 

Le mot-clé volatile permet en Java d'interdire aux compilateurs de réaliser certaines optimisations de code telles que le déplacement des variables du tas vers la pile. Ce genre d'optimisation, dans la plupart des cas n'a aucun effet nocif sur le déroulement de 99% de vos applications, mais dans certaines circonstances, cela peut mener à bien des casses-têtes difficles à résoudre.  

Il existe des différences fondamentales dans la sémantique de l'ordre volatile en C# et Java qui sont illustrées dans l'exemple ci-dessous tiré de l'article suivant : The "Double-Checked Locking is Broken" Declaration

C# Code

/* Used to lazily instantiate a singleton class */

/*             WORKS AS EXPECTED                */

class Foo

{

  private volatile Helper helper = null;

  public Helper getHelper()

  {

         if (helper == null)

         {

                 lock(this)

                 {

                         if (helper == null)

                                helper = new Helper();

                 }

         }

         return helper;

  }

}

 
Java Code

/* Used to lazily instantiate a singleton class */

/* BROKEN UNDER CURRENT SEMANTICS FOR VOLATILE */

class Foo

{

  private volatile Helper helper = null;

  public Helper getHelper()

  {

         if (helper == null)