web-dev-qa-db-fra.com

Est-il permis d'utiliser une implémentation d'interface explicite pour masquer les membres en C #?

Je comprends comment travailler avec des interfaces et une mise en œuvre explicite de l'interface en C #, mais je me demandais si elle est considérée comme une forme de 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, tout en étant utiles, seraient très rarement utilisés. Ainsi, je pensais qu'il serait logique de les cacher d'une classe d'implémentation via une mise en œuvre explicite pour éviter l'encombrement de l'intellensense.

14
Kyle Baran

Oui, c'est généralement une mauvaise forme.

Ainsi, je pensais qu'il serait logique de les cacher d'une classe d'implémentation via une mise en œuvre explicite pour éviter l'encombrement de l'intellensense.

Si votre intellisense est trop encombré, votre classe est trop grande. Si votre classe est trop grande, cela fait probablement trop de choses et/ou mal conçus.

Fixez votre problème, pas votre symptôme.

39
Telastyn

Utilisez la fonctionnalité pour ce qu'elle était destinée. Réduire l'encombrement des espaces de noms n'est pas un de ceux-ci.

Les raisons légitimes ne sont pas à propos de la "cachette" ou de l'organisation, elles doivent résoudre l'ambiguïté et se spécialiser. Si vous implémentez deux interfaces qui définissent "FOO", la sémantique de FOO pourrait différer. Vraiment pas un meilleur moyen de résoudre cela, à l'exception des interfaces explicites. L'alternative consiste à interdire la mise en œuvre des interfaces qui se chevauchent ou à déclarer que les interfaces ne peuvent pas associer la sémantique (ce qui n'est qu'une chose conceptuelle de toute façon). Les deux sont de mauvaises alternatives. Parfois, la praticité l'emporte sur l'élégance.

Certains auteurs/experts considèrent comme un kilomètre d'une fonctionnalité (les méthodes ne font pas vraiment partie du modèle d'objet du type). Mais comme toutes les caractéristiques, quelque part, quelqu'un en a besoin.

Encore une fois, je voudrais non l'utiliser pour cacher ou organisation. Il existe des moyens de cacher des membres d'IntelliSense.

[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
11
codenheim

Je pourrais être un signe de code désordonné, mais parfois le monde réel est en désordre. Un exemple de .NET Framework est ReadOnlyColection qui masque le setter mutation [], ajouter et set.

I.e ReadOnlycollection implémente ilist <t>. Voir aussi - ma question à SO Il y a plusieurs années quand je réfléchis à cela.

5

C'est une bonne forme à le faire lorsque le membre est inutile dans le contexte. Par exemple, si vous créez une collection lisonly qui implémente IList<T> En déléguant à un objet interne _wrapped alors vous pourriez avoir quelque chose comme:

public T this[int index]
{
  get
  {
     return _wrapped[index];
  }
}
T IList<T>.this[int index]
{
  get
  {
    return this[index];
  }
  set
  {
    throw new NotSupportedException("Collection is read-only.");
  }
}
public int Count
{
  get { return _wrapped.Count; }
}
bool ICollection<T>.IsReadOnly
{
  get
  {
    return true;
  }
}

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, bien sûr, il n'est bien sûr pas une mise en œuvre explicite, bien que notez qu'il arrive à être similaire à la lecture-écriture T this[int index] défini dans l'interface mais est en lecture seule.

T IList<T>.this[int index] est explicite car une partie de celle-ci (le getter) est parfaitement assortie par la propriété ci-dessus, et l'autre partie jettera toujours une exception. Bien que vital pour une personne accédant à une instance de cette classe à travers l'interface, il est inutile de l'utiliser par une variable du type de classe.

De même parce que bool ICollection<T>.IsReadOnly va toujours retourner vrai que cela est totalement inutile à code qui est écrit contre le type de classe, mais pourrait être essentiel à celui-ci à l'aide du type de l'interface et que nous l'appliquons donc explicitement.

Inversement, public int Count n'est pas implémenté explicitement car il pourrait potentiellement être utile à une personne utilisant une instance à travers son propre type.

Mais avec votre cas "très rarement utilisé", je me pencherais très fortement pour ne pas utiliser de mise en œuvre explicite.

Dans les cas où je recommande d'utiliser une implémentation explicite appelant la méthode via une variable du type de classe serait soit une erreur (tenter d'utiliser le setter indexé), soit inutile (vérifiant une valeur qui sera toujours la même) alors en cachette Ils protègent l'utilisateur du code buggy ou sous-optimal. C'est nettement différent du code que vous pensez être juste susceptible d'être rarement utilisé. Pour cela, je pourrais envisager d'utiliser l'attribut EditorBrowsable pour masquer le député d'IntelliSense, bien que même que je serais fatigué de; Les cerveaux des gens ont déjà leur propre logiciel pour filtrer ce qui ne les intéresse pas.

2
Jon Hanna