Car personne d'autre ne explicitement prévu cette réponse, je vais ajouter ce qui suit:
La mise en œuvre d'une interface sur une structure n'a pas de conséquences négatives que ce soit.
Toute variable du type d'interface utilisé pour tenir une struct entraînera dans un coffret de la valeur de cette structure utilisée. Si la structure est immuable (une bonne chose), c'est au pire un problème de performance, sauf si vous êtes:
- à l'aide de l'objet résultant de verrouillage (une extrêmement mauvaise idée de toute façon)
- à l'aide de la référence à l'égalité sémantique et en espérant que cela fonctionne pour les deux en boîte des valeurs de la même structure.
Ces deux serait peu probable, au lieu de cela, vous êtes susceptible de faire une des actions suivantes:
Les génériques
Peut-être que beaucoup des raisons raisonnables pour les structures de mise en œuvre des interfaces est de sorte qu'ils peuvent être utilisés à l'intérieur d'un générique contexte avec des contraintes. Dans ce mode la variable comme ceci:
class Foo<T> : IEquatable<Foo<T>> where T : IEquatable<T>
{
private readonly T a;
public bool Equals(Foo<T> other)
{
return this.a.Equals(other.a);
}
}
- Permettre l'utilisation de la structure en tant que paramètre de type
- tant qu'aucune autre contrainte comme
new()
ou class
est utilisé.
- Permettre à l'évitement de la boxe sur les structures utilisées de cette façon.
Alors ce.une n'est PAS une référence d'interface ainsi, il ne provoque pas une boîte de ce qui est mis en elle. Plus loin, quand le compilateur c# compile les classes génériques et les besoins pour insérer les invocations de l'instance des méthodes définies dans les instances du paramètre de Type T, il peut utiliser la contrainte opcode:
Si thisType est un type de valeur et thisType implémente la méthode, puis ptr est passé non modifiée comme " ce " pointeur à l'appel de la méthode d'instruction, pour la mise en œuvre de la méthode par thisType.
Cela évite la boxe et depuis le type de valeur est la mise en œuvre de l'interface doit implémenter la méthode, donc pas de boxe aura lieu. Dans l'exemple ci-dessus l' Equals()
invocation est fait avec pas de case sur cette.un1.
Faible frottement de l'API
La plupart des structures devrait avoir primitive comme sémantique où bit à bit des valeurs identiques sont considérés comme égauxà 2. Le moteur d'exécution de l'approvisionnement de tels comportements dans l'implicite Equals()
, mais cela peut être long. Aussi l'implicite de l'égalité est pas exposé comme une œuvre d' IEquatable<T>
et empêche ainsi les structs être facilement utilisés comme clés pour les Dictionnaires, à moins qu'ils explicitement l'appliquer eux-mêmes. Par conséquent, il est commun pour beaucoup de public struct types de déclarer qu'ils mettent en IEquatable<T>
(où T
est eux-même) pour rendre cela plus facile et plus performant ainsi que la cohérence avec le comportement de nombreux types de valeur dans le CLR BCL.
Toutes les primitives de la BCL mettre en œuvre au minimum:
IComparable
IConvertible
IComparable<T>
-
IEquatable<T>
(Et donc de IEquatable
)
Nombreux aussi mettre en oeuvre IFormattable
, plus de nombreux définies par le Système de valeur de type DateTime, plage de Temps, et Guid de mettre en œuvre la plupart ou la totalité de ces derniers aussi bien. Si vous êtes à la mise en œuvre d'une de même très utile " type comme un nombre complexe struct ou certains de largeur fixe textuelle des valeurs de mise en œuvre de plusieurs de ces interfaces communes (correctement) feront de votre struct plus utile et utilisable.
Exclusions
Évidemment, si l'interface implique fortement la mutabilité (comme ICollection
) puis de la mettre en œuvre est une mauvaise idée car cela signifierait tat vous faites soit la structure mutable (conduisant à des sortes d'erreurs décrites ci-dessus où les modifications se produisent sur la boîte de la valeur plutôt que l'original), ou vous induire les utilisateurs en erreur en ignorant les implications des méthodes comme l' Add()
ou de lever des exceptions.
De nombreuses interfaces n'implique PAS la mutabilité (comme IFormattable
) et servir le idiomatiques manière d'exposer un certain nombre de fonctionnalités, de manière uniforme. Souvent l'utilisateur de la structure n'aura pas à s'inquiéter de boxe aériennes pour un tel comportement.
Résumé
Quand fait raisonnablement, sur immuable types de valeur, la mise en œuvre de l'utilité des interfaces est une bonne idée
Notes:
1: Notez que le compilateur peut utiliser ce moment de l'invocation de méthodes virtuelles sur des variables qui sont connus pour être spécifique d'un type struct mais dans lequel il est nécessaire d'invoquer une méthode virtuelle. Par exemple:
List<int> l = new List<int>();
foreach(var x in l)
;//no-op
L'agent recenseur renvoyé par la Liste est une structure, d'une optimisation pour éviter une allocation lors de l'énumération de la liste (Avec d'intéressantes conséquences). Cependant, la sémantique de foreach préciser que si l'agent recenseur implémente IDisposable
alors Dispose()
sera appelée une fois l'itération est terminée. Bien évidemment cela se fait par le biais d'une boîte d'appel permettrait d'éliminer toutes les prestations de l'agent recenseur être un struct (en fait, il serait pire). Pire, si disposer d'appel modifie l'état de l'agent recenseur, d'une certaine façon alors ce qui allait se passer sur la boîte d'instance et de nombreux bogues subtils pourrait être introduit dans les cas complexes. Donc la IL émise dans ce genre de situation est la suivante:
IL_0001: newobj Système.Les Collections.Génériques.Liste..ctor
IL_0006: stloc.0
IL_0007: nop
IL_0008: ldloc.0
IL_0009: callvirt Système.Les Collections.Génériques.Liste.GetEnumerator
IL_000E: stloc.2
IL_000F: br.s IL_0019
IL_0011: ldloca.s 02
IL_0013: Système d'appel.Les Collections.Génériques.Liste.get_Current
IL_0018: stloc.1
IL_0019: ldloca.s 02
IL_001B: Système d'appel.Les Collections.Génériques.Liste.MoveNext
IL_0020: stloc.3
IL_0021: ldloc.3
IL_0022: brtrue.s IL_0011
IL_0024: quitter.s IL_0035
IL_0026: ldloca.s 02
IL_0028: contraint. Système.Les Collections.Génériques.Liste.Énumérateur
IL_002E: callvirt Système.IDisposable.Disposer
IL_0033: nop
IL_0034: endfinally
Ainsi, la mise en œuvre de IDisposable ne cause pas de problèmes de performances et de la (regrettable) mutable aspect de l'agent recenseur est conservé si la méthode dispose de réellement faire quelque chose!
2: double et float sont des exceptions à cette règle lorsque les valeurs NaN ne sont pas considérées comme égales.