È permesso usare l'implementazione esplicita dell'interfaccia per nascondere i membri in C #?

13

Capisco come lavorare con le interfacce e l'implementazione esplicita dell'interfaccia in C #, ma mi chiedevo se fosse considerata una cattiva forma per nascondere alcuni membri che non sarebbero stati usati frequentemente. Ad esempio:

public interface IMyInterface
{
    int SomeValue { get; set; }
    int AnotherValue { get; set; }
    bool SomeFlag { get; set; }

    event EventHandler SomeValueChanged;
    event EventHandler AnotherValueChanged;
    event EventHandler SomeFlagChanged;
}

So in anticipo che gli eventi, sebbene utili, sarebbero usati molto raramente. Quindi, ho pensato che avrebbe senso nasconderlo lontano da una classe di implementazione tramite un'implementazione esplicita per evitare l'ingombro di IntelliSense.

    
posta Kyle Baran 27.10.2014 - 21:42
fonte

4 risposte

39

Sì, è in genere una cattiva forma.

Thus, I thought it would make sense to hide them away from an implementing class via explicit implementation to avoid IntelliSense clutter.

Se IntelliSense è troppo ingombrante, la tua classe è troppo grande. Se la tua classe è troppo grande, è probabile che faccia troppe cose e / o mal progettato.

Risolvi il problema, non il tuo sintomo.

    
risposta data 27.10.2014 - 21:58
fonte
11

Utilizzare la funzione per ciò a cui era destinata. Ridurre la confusione nello spazio dei nomi non è uno di quelli.

Le ragioni legittime non riguardano il "nascondersi" o l'organizzazione, ma sono la risoluzione dell'ambiguità e la specializzazione. Se si implementano due interfacce che definiscono "Foo", la semantica di Foo potrebbe essere diversa. In realtà non è un modo migliore per risolvere questo, tranne per le interfacce esplicite. L'alternativa è non consentire l'implementazione di interfacce sovrapposte o dichiarare che le interfacce non possono associare la semantica (che è comunque solo una cosa concettuale). Entrambe sono povere alternative. A volte la praticità supera l'eleganza.

Alcuni autori / esperti lo considerano un kludge di una caratteristica (i metodi non fanno realmente parte del modello a oggetti del tipo). Ma come tutte le funzionalità, da qualche parte, qualcuno ne ha bisogno.

Ancora una volta, vorrei non usarlo per nascondigli o organizzazione. Ci sono modi per nascondere i membri da intellisense.

[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
    
risposta data 27.10.2014 - 22:07
fonte
5

Potrei essere un segno di codice disordinato, ma a volte il mondo reale è disordinato. Un esempio di .NET framework è ReadOnlyColection che nasconde il mutante setter [], Aggiungi e Imposta.

I.E ReadOnlyCollection implementa IList < T & gt ;. Vedi anche la mia domanda a SO molti anni fa quando ho riflettuto su questo.

    
risposta data 27.10.2014 - 22:00
fonte
2

È una buona forma farlo quando il membro non ha senso nel contesto. Ad esempio, se crei una raccolta di sola lettura che implementa IList<T> delegando a un oggetto interno _wrapped , potresti avere qualcosa del tipo:

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;
  }
}

Qui abbiamo quattro casi diversi.

public T this[int index] è definito dalla nostra classe piuttosto che dall'interfaccia, e quindi non è ovviamente un'implementazione esplicita, sebbene si noti che è simile al read-write T this[int index] definito nell'interfaccia ma viene letto -Solo.

T IList<T>.this[int index] è esplicito perché una parte di esso (il getter) è perfettamente abbinata alla proprietà sopra, e l'altra parte genererà sempre un'eccezione. Sebbene sia di vitale importanza per qualcuno che accede a un'istanza di questa classe attraverso l'interfaccia, è inutile che qualcuno lo usi attraverso una variabile del tipo della classe.

Analogamente, poiché bool ICollection<T>.IsReadOnly restituirà sempre true, è assolutamente inutile codificare ciò che è scritto rispetto al tipo della classe, ma potrebbe essere essenziale per utilizzarlo attraverso il tipo di interfaccia, e quindi lo implementiamo esplicitamente.

Al contrario, public int Count non viene implementato esplicitamente perché potrebbe essere potenzialmente utile a qualcuno che utilizza un'istanza tramite il proprio tipo.

Ma con il tuo caso di "uso molto raro", mi prendo molto in considerazione per non utilizzare un'implementazione esplicita.

Nei casi in cui raccomando l'uso di un'implementazione esplicita, chiamare il metodo attraverso una variabile del tipo della classe potrebbe essere un errore (tentare di usare il setter indicizzato) o inutile (controllare un valore che sarà sempre lo stesso) così nascondendoli si sta proteggendo l'utente da codice bacato o sub-ottimale. Questo è significativamente diverso dal codice che pensi sia probabilmente usato raramente . Per questo potrei considerare di usare l'attributo EditorBrowsable per nascondere il membro da intellisense, anche se ne sarei stanco; I cervelli delle persone hanno già il loro software per filtrare ciò che non li interessa.

    
risposta data 28.10.2014 - 14:13
fonte

Leggi altre domande sui tag