Quand les méthodes d’extension se cassent-elles?

Nous discutons actuellement de la question de savoir si les méthodes d’extension dans .NET sont mauvaises ou non. Ou dans quelles circonstances les méthodes d’extension peuvent présenter des difficultés pour trouver des bogues ou se comporter de manière inattendue.

Nous sums venus avec:

  • Écrire une méthode d’extension pour des types qui ne sont pas sous votre contrôle (par exemple, étendre DirectoryInfo avec GetTotalSize (), etc.) est mauvais, car le propriétaire de l’API peut introduire une méthode qui cache notre extension – et peut avoir différents cas . Par exemple, le test de null dans une méthode d’extension se traduira automatiquement par une exception NullReferenceException si la méthode d’extension n’est plus utilisée en raison du masquage.

Question:

  • Existe-t-il d’autres situations dangereuses que la “dissimulation” auxquelles nous ne pensons pas?

Modifier:

Une autre situation très dangereuse. Supposons que vous ayez une méthode d’extension:

namespace Example.ExtensionMethods { public static class Extension { public static int Conflict(this TestMe obj) { return -1; } } } 

Et utilisez-le:

 namespace Example.ExtensionMethods.Conflict.Test { [TestFixture] public class ConflictExtensionTest { [Test] public void ConflictTest() { TestMe me = new TestMe(); int result = me.Conflict(); Assert.That(result, Is.EqualTo(-1)); } } } 

Notez que l’espace de noms sur lequel vous l’utilisez est plus long.

Maintenant, vous référencez une dll avec ceci:

 namespace Example.ExtensionMethods.Conflict { public static class ConflictExtension { public static int Conflict(this TestMe obj) { return 1; } } } 

Et votre test échouera! Il va comstackr sans erreur de compilation. Il va simplement échouer . Sans même avoir à spécifier “using Example.ExtensionMethods.Conflict”. Le compilateur explorera le nom de l’espace de noms et trouvera Example.ExtensionMethods.Conflict.ConflictExtension avant Example.ExtensionMethods.Extension et l’utilisera sans jamais se plaindre de méthodes d’extension ambiguës . Oh l’horreur!

Quelques curiosités:

  • les méthodes d’extension peuvent être appelées sur null instances null ; cela peut être déroutant (mais parfois utile)
  • la question “cacher” est un problème si elles ont une intention différente
  • de même, vous pouvez obtenir une méthode d’extension différente portant le même nom à partir de 2 espaces de noms différents; si vous n’avez qu’un seul des deux espaces de noms, cela pourrait entraîner un comportement incohérent (selon lequel) …
  • … mais si quelqu’un ajoute une méthode d’extension similaire (même signature) dans un deuxième espace de noms utilisé par votre code, elle se rompra au moment de la compilation (ambiguë)

( edit ) Et bien sûr, il y a la NullableNullable / new() ” ( voir ici ) …

Je ne suis pas d’accord, le but des méthodes d’extension est d’append vos membres aux classes en boîte noire. Comme tout le rest, il y a des pièges, vous devez être attentif lors de la désignation, de la mise en œuvre et comprendre l’ordre hiérarchique des méthodes.

Une rupture que nous venons de trouver dans le projet MoreLINQ : si vous écrivez une méthode d’extension générique, il est impossible de vous assurer qu’elle fonctionnera avec tous les types. Nous avons une méthode avec cette signature:

 public static IEnumerable Concat(this T head, IEnumerable tail) 

Vous ne pouvez pas l’utiliser avec:

 "foo".Concat(new [] { "tail" }); 

à cause de la méthode ssortingng.Concat

J’ai utilisé Ruby on Rails depuis presque aussi longtemps que j’ai utilisé C #. Ruby vous permet de faire quelque chose de similaire aux nouvelles méthodes d’extension. Il y a bien sûr des problèmes potentiels si quelqu’un appelle la méthode de la même manière, mais les avantages de pouvoir append des méthodes à une classe fermée l’emportent largement sur le désavantage potentiel (qui serait probablement causé par une mauvaise conception ou une mauvaise planification).

Pour vous assurer que les méthodes d’extension ne sont pas en conflit avec d’autres méthodes (extension ou autre), vous pouvez utiliser FxCop avec des règles telles que Empêcher les signatures de méthode d’extension dupliquées .

Tout d’abord, je pense que votre libellé est un peu trompeur. Je suppose que vous parlez de “types” et non d ‘”objects”.

Deuxièmement, le gros avantage des méthodes d’extension est que vous pouvez append des fonctionnalités à des types que vous ne contrôlez pas. Si vous contrôlez le type, pourquoi ne pas simplement modifier le type au lieu de recourir à des méthodes d’extension?

Dans mon équipe, nous avons adopté l’attitude selon laquelle les méthodes d’extension sont si utiles que vous ne pouvez pas les interdire de manière réaliste, mais si dangereuses (principalement en raison du problème de la dissimulation) qu’il faut être un peu prudent. Nous avons donc décidé que tous les noms de méthodes d’extension devaient être précédés de X (nous avons donc un tas de XInit...() pour initialiser les contrôles de manière utile, par exemple). De cette façon, a) la probabilité d’une collision de nommage est réduite et b) le programmeur sait qu’il utilise une méthode d’extension et non une méthode de classe.

Ce que les méthodes d’extension .Net sont aussi une forme limitée de MonkeyPatching (essayez d’ignorer le php rant qui s’y trouve).

Cela devrait vous donner de la matière pour votre discussion.