Tu connais certainement Java beaucoup mieux que moi, mais c'est pas toi qui vas m'apprendre C# ;)
Je sais bien que les méthodes d'extension ne sont pas réellement ajoutées au type ; mais le fait qu'elles s'utilisent comme si c'étaient des méthodes d'instance permet de faire quasiment comme si c'était vraiment le cas. Le fait qu'elles apparaissent dans l'autocomplétion les rend aussi facilement découvrables : tout d'un coup tu as plein de nouvelles méthodes sur les types qui implémentent IEnumerable<T> (par exemple).
Après bien sûr c'est juste du sucre syntaxique pour appeler des méthodes statique, mais ça rend le code vraiment plus clair, notamment parce que ça permet le chainage ; par exemple on peut écrire :
Ce qui est quand même nettement plus lisible que l'équivalent écrit avec des appels de méthodes statiques "normaux" :Code:
1
2 IEnumerable<string> namesOfChildren = people.Where(p => p.Age < 18) .Select(p => p.Name);
Après c'est sûr que c'est moins puissant que les defender methods de Java, dans la mesure où ça ne supporte pas le polymorphisme (puisque c'est statique, comme tu l'as souligné). Mais ça a l'avantage que tu peux "étendre" des types existants dont tu ne contrôles pas le code source, alors que tu ne peux ajouter une defender method qu'à une interface que tu contrôles.Code:
1
2
3
4
5
6IEnumerable<string> namesOfChildren = Enumerable.Select( Enumerable.Where( people, p => p.Age < 18), p => p.Name);
Pour ce qui est de l'accès aux membres privés ou protégés, les implémentations qui substituent la defender method peuvent le faire bien sûr, mais pas l'implémentation par défaut définie dans l'interface... Du coup, une defender method qui n'est pas substituée est fonctionnellement équivalente à une méthode d'extension ; c'est la possibilité de substitution qui fait finalement toute la différence.

