[C# academy] De Java à C# – le point de vue d’un javaiste

Un rapport d’étonnement

Après quelques années à travailler dans un contexte exclusivement Java, j’ai depuis maintenant un mois l’opportunité de changer de casquette et de travailler sur un projet .Net. Cette expérience, riche en enseignements, représente pour moi un nouveau challenge : l’apprentissage du langage C#. Je vous propose d’explorer ce qui en fait sa richesse à travers le prisme d’un javaiste.

L’article n’a pas vocation à lister toutes les différences entre ces deux langages mais à fournir un rapport d’étonnement naïf des points les plus intéressants de C#.

Les deux langages partagent évidement beaucoup de choses de par leurs syntaxes, ils sont assez proches sur la manière de déclarer des classes, des interfaces, des types génériques, etc. En revanche C# est plus riche en termes de possibilités offertes par rapport à Java.

Ce qui me plait particulièrement c’est que sa courbe d’apprentissage est progressive en fonction des concepts que l’on choisi d’explorer, contrairement à Java où les spécificités natives sont relativement vite assimilées. Ce qui me plait moins ce sont les conventions de nommages assez perturbantes pour un javaiste (Upper Camel Case) 😉

Petit tour d’horizon.

 

Créer des objets facilement

En java instancier un objet par son constructeur est fastidieux si les paramètres sont nombreux cela devient peu lisible. On peut alors utiliser le concept de l’api fluent, ce qui est mieux mais plus verbeux. C# propose une approche simple :

Store s = new Store {
        Id = 1,
        Name = "AppleStore"
      } 

Et cela devient particulièrement intéressant avec les collections:

List<StudentName> students = new List<StudentName>()
{
  new StudentName {FirstName="Craig", LastName="Playstead", ID=116},
  new StudentName {FirstName="Shu", LastName="Ito", ID=112},
  new StudentName {FirstName="Gretchen", LastName="Rivas", ID=113},
  new StudentName {FirstName="Rajesh", LastName="Rotti", ID=114}
};

 

Eliminer le côté verbeux des getter/setter

En Java il est courant de devoir ajouter des getter/setter en plus, même si aujourd’hui ils sont générés (eclipse, lombok) cela reste assez verbeux pour peu de valeur ajouté. C# possède un concept de property qui dans sa forme condensée devient autoproperty et encapsule pour vous les getter/setter sans avoir à les définir.

public string Name { get; set; }

Je trouve la concision de cette syntaxe assez appréciable. Bien sur il est possible de définir un corps aux méthodes get et set, en revenant dans le cas classique d’une property.

 

Ajouter des fonctionnalités à une API que l’on ne possède pas

Pour illustrer ce concept l’exemple classique est de rajouter des fonctions à la classe String, donnant ainsi la possibilité d’invoquer ces fonctions sur une instance de String comme si elles étaient natives. C’est assez fort de pouvoir étendre une API aussi facilement. Plutôt sympathique.

Pour créer une extension method, on définit une simplement une méthode statique (dans une classe statique) avec un paramètre du type que l’on souhaite « étendre » précédé du mot clé this. Exemple:

public static string UppercaseFirstLetter(this string value) {...}
string value = "dot net perls";
value = value.UppercaseFirstLetter();

Un autre cas d’utilisation est de rajouter du comportement basique sur des enum, par exemple, un pretty print comme illustré dans cet exemple http://blogs.msdn.com/b/abhinaba/archive/2005/10/20/c-enum-and-overriding-tostring-on-it.aspx

 

Manipuler des collections facilement Linq et lambda

Cela devient frustrant quand on repasse en Java (et donc surtout quand on n’est pas en java 8) de devoir traiter des collection « à la main / à l’ancienne » c’est-à-dire avec des traitements for / if ,etc.

Car on s’habitue vite au confort de cette syntaxe :

customers.Where(c => c.City == "London");

Le principe du Select est particulièrement intéressant pour transformer une collection via une lambda expression :

IEnumerable<SelectListItem> stores = database.Stores
        .Where(store => store.CompanyID == curCompany.ID)
        .Select(store => new SelectListItem { Value = store.Name, Text = store.ID });

Ou en appelant une méthode, par exemple dans un cas classique de transformation d’une liste d’objets en DTO :

IEnumerable<SelectListItem> stores = database.Stores.Select(StoreToDto);

private SelectListItem StoreToDto(Store s) {...} // retourne un objet SelectListItem à partir d'un Store

Il faut faire attention à l’interface IEnumerable qui est Lazy contrairement à un tableau ou une liste.

 

Jouer avec les pointeurs de fonctions

Les délégués (deleguates) sont grosso modo la possibilité de définir un pointeur de fonction. La déclaration d’un type délégué est semblable à une signature de méthode.

private delegate void ConfigureStore(Store s);

Ensuite il est possible d’instancier ce délégué comme un objet mais en fournissant le nom de la méthode que le délégué encapsule. Exemple :

ConfigureStore deleg = new ConfigureStore(PrintStore);
static void PrintStore(Store s)
 {
        Console.WriteLine(s);
 }

ou via une expression lambda :

ConfigureStore lambda = s => Console.WriteLine(s);

Un fois le délégué crée, il s’invoque avec les paramètres déclarés dans sa signature, exemple:

deleg(new Store {Id = 1, Name = "Apple Store"} );

ou

lambda(new Store {Id = 1, Name = "Apple Store"} );

 

Depuis c#3: les Func permettent de créer des délégués plus agréables à manipuler. La technique est équivalente mais moins verbeuse. En revanche les Func ne permettent pas de retourner un type Void, il faut pour cela passer par les Action. Ex de Func :

Func<string, string> convert = s => s.ToUpper();
Console.WriteLine(convert("toto")); // TOTO

Les Func sont aussi au coeur de l’API Expression qui permet de créer des requêtes dynamiques. Les requêtes dynamiques sont utiles lorsque les caractéristiques d’une requête ne sont pas connues lors de la compilation.

 

Les conversions faciles

La aussi la fonctionnalité est intéressante. Les implicit/explicit operator permettent de définir une fonction de conversion entre 2 objets.

Par exemple, imaginons devoir convertir un objet Store en StoreDto on pourrait créer un operator implicit qui effectue la conversion pour nous:

class StoreDto
 {
   public string Name { get; set; }
   public static implicit operator StoreDto(Store s)
   {
     return new StoreDto { Name = s.Name };
   }
 }

Il devient alors possible de simplement « caster » un Store en StoreDto :

StoreDto dto = (StoreDto) unStore;

 

Itérer sur ce que vous retournez au moment où vous l’utilisez

La formulation n’est pas très claire je l’admets volontiers :) Cependant le yield est un concept très intéressant qui mérite que l’on s’y arrête. Le principe est de construire un bloc itérateur via une méthode. Pour cela on expose des objets grâce à l’expression return yield, donnant ainsi la possibilité de les consommer à travers une boucle IEnumerable. En voici un exemple concret mais pas très utile :

public IEnumerable<int> Integers()
{
    yield return 1;
    yield return 2;
    yield return 4;
    yield return 8;
    yield return 16;
    yield return 16777216;
}

L’avantage est de pouvoir disposer des éléments parcourus dans l’ordre que l’on souhaite, cela assez facilement, comme illustré dans l’exemple ci-dessous. On peut aussi utiliser cette technique pour effectuer le parcours d’un arbre selon un sens donné, profondeur d’abord, largeur d’abord. Ou enfin parcourir une liste par blocs (chunks) de N éléments, comme illustré ici http://www.programminginterviews.info/2012/05/explain-c-yield-keyword-with-example.html.

IEnumerable<object> EfficientMerge(List<object> list1, List<object> list2) {
    foreach(var o in list1) 
        yield return o; 
    foreach(var o in list2) 
        yield return o;
}

Mais le principal avantage est de pouvoir créer une énumération en lazy, et de la « streamer » sans pénaliser la consommation mémoire car en ne récupérant/stockant qu’une seule ligne à la fois. C’est particulièrement efficace si on a besoin de ne récupérer que les 5 premières lignes d’une liste, ou si la liste est très volumineuse.

 

Conclusion

Il existe beaucoup d’autres concepts que je n’ai pas eu le temps d’explorer ici. En revanche j’espère que ce petit aperçu pourra donner envie aux javaistes de découvrir ce langage qui possède quelques atouts indéniables. Pour plus d’informations, la page suivante est aussi une bonne introduction pour un public de javaistes :

https://msdn.microsoft.com/fr-fr/library/ms228602(v=vs.90).aspx

 

 

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *