Je comprends comment travailler avec les interfaces et l'implémentation d'interface explicite en C #, mais je me demandais si c'était considéré comme une mauvaise forme pour cacher certains membres qui ne seraient pas utilisés fréquemment. Par exemple:
public interface IMyInterface
{
int SomeValue { get; set; }
int AnotherValue { get; set; }
bool SomeFlag { get; set; }
event EventHandler SomeValueChanged;
event EventHandler AnotherValueChanged;
event EventHandler SomeFlagChanged;
}
Je sais à l'avance que les événements, bien qu'utiles, seraient très rarement utilisés. Ainsi, j'ai pensé qu'il serait logique de les cacher à une classe d'implémentation via une implémentation explicite pour éviter l'encombrement IntelliSense.
c#
interfaces
implementations
Kyle Baran
la source
la source
Réponses:
Oui, c'est généralement une mauvaise forme.
Si votre IntelliSense est trop encombré, votre classe est trop grande. Si votre classe est trop grande, elle fait probablement trop de choses et / ou est mal conçue.
Résolvez votre problème, pas votre symptôme.
la source
Utilisez la fonction pour ce à quoi elle était destinée. La réduction de l'encombrement des espaces de noms n'en fait pas partie.
Les raisons légitimes ne concernent pas la «dissimulation» ou l'organisation, elles visent à lever l'ambiguïté et à se spécialiser. Si vous implémentez deux interfaces qui définissent "Foo", la sémantique de Foo peut différer. Vraiment pas un meilleur moyen de résoudre ce problème, sauf pour les interfaces explicites. L'alternative consiste à interdire l'implémentation d'interfaces qui se chevauchent, ou à déclarer que les interfaces ne peuvent pas associer la sémantique (ce qui est de toute façon conceptuel). Les deux sont de piètres alternatives. Parfois, la praticité l'emporte sur l'élégance.
Certains auteurs / experts le considèrent comme un kludge d'une fonctionnalité (les méthodes ne font pas vraiment partie du modèle d'objet du type). Mais comme toutes les fonctionnalités, quelque part, quelqu'un en a besoin.
Encore une fois, je ne l' utiliserais pas pour me cacher ou organiser. Il existe des moyens de masquer les membres à Intellisense.
la source
IDisposable.Dispose
fait quelque chose mais est donné un nom différent commeClose
, je considère qu'il est parfaitement raisonnable pour une classe dont le contrat rejette expressément les états que le code peut créer et abandonner des instances sans appelerDispose
à utiliser l'implémentation d'interface explicite pour définir uneIDisposable.Dispose
méthode qui ne fait rien.Je peux être un signe de code en désordre, mais parfois le monde réel est en désordre. Un exemple du framework .NET est ReadOnlyColection qui masque le setter de mutation [], Add et Set.
IE ReadOnlyCollection implémente IList <T>. Voir aussi ma question à SO il y a plusieurs années lorsque j'ai réfléchi à cela.
la source
ConcurrentDictionary
, qui implémente divers membres deIDictionary<T>
manière explicite afin que les consommateurs deConcurrentDictionary
A) ne les appellent pas directement et B) puissent utiliser des méthodes qui nécessitent une implémentation deIDictionary<T>
si absolument nécessaire. Par exemple, les utilisateurs deConcurrentDictionary<T>
devraient appelerTryAdd
plutôt queAdd
d'éviter d'avoir besoin d'exceptions inutiles.Add
en œuvre explicite réduit également l'encombrement.TryAdd
peut faire n'importe quoiAdd
(Add
est implémenté comme un appel àTryAdd
, donc fournir les deux serait redondant). Cependant, aTryAdd
nécessairement un nom / signature différent, il n'est donc pas possible de l'implémenterIDictionary
sans cette redondance. L'implémentation explicite résout ce problème proprement.Il est bon de le faire lorsque le député est inutile dans son contexte. Par exemple, si vous créez une collection en lecture seule qui implémente
IList<T>
en déléguant à un objet interne,_wrapped
vous pouvez avoir quelque chose comme:Ici, nous avons quatre cas différents.
public T this[int index]
est défini par notre classe plutôt que par l'interface, et n'est donc bien sûr pas une implémentation explicite, bien que cela se trouve être similaire à la lecture-écritureT this[int index]
définie dans l'interface mais est en lecture seule.T IList<T>.this[int index]
est explicite car une partie de celui-ci (le getter) est parfaitement mise en correspondance par la propriété ci-dessus, et l'autre partie lèvera toujours une exception. Bien que vital pour quelqu'un accédant à une instance de cette classe via l'interface, il est inutile pour quelqu'un de l'utiliser via une variable du type de la classe.De même, parce que
bool ICollection<T>.IsReadOnly
va toujours retourner vrai, il est tout à fait inutile de code écrit contre le type de la classe, mais pourrait être vital pour celui qui l'utilise via le type de l'interface, et donc nous l'implémentons explicitement.Inversement,
public int Count
n'est pas implémenté explicitement car il pourrait potentiellement être utile à quelqu'un utilisant une instance via son propre type.Mais avec votre cas "très rarement utilisé", je pencherais très fortement pour ne pas utiliser une implémentation explicite.
Dans les cas où je recommande d'utiliser une implémentation explicite appelant la méthode via une variable du type de la classe, ce serait soit une erreur (essayer d'utiliser le setter indexé) soit inutile (vérifier une valeur qui sera toujours la même) donc en se cachant vous protégez l'utilisateur du buggy ou du code sous-optimal. C'est très différent du code qui, selon vous, est susceptible d'être rarement utilisé. Pour cela, je pourrais envisager d'utiliser l'
EditorBrowsable
attribut pour cacher le membre d'intellisense, même si je serais fatigué de; le cerveau des gens a déjà son propre logiciel pour filtrer ce qui ne les intéresse pas.la source
IsReadOnly
propriété?