Domanda

Le nuove estensioni .Net 3.5 consentire la funzionalità di essere separato dal interfacce.

Per esempio in .Net 2.0

public interface IHaveChildren {
    string ParentType { get; }
    int ParentId { get; }

    List<IChild> GetChildren()
}

Possibile (in 3.5) diventano:

public interface IHaveChildren {
    string ParentType { get; }
    int ParentId { get; }
}

public static class HaveChildrenExtension {
    public static List<IChild> GetChildren( this IHaveChildren ) {
        //logic to get children by parent type and id
        //shared for all classes implementing IHaveChildren 
    }
}

Questo mi sembra un meccanismo migliore per molte interfacce.Non c'è più bisogno di una base astratta per la condivisione di questo codice, e, funzionalmente, il codice funziona lo stesso.Questo potrebbe rendere il codice più stabile e più facile da testare.

Il solo svantaggio è che un astratto basi di attuazione può essere virtuale, ma che può essere lavorato in giro (sarebbe un metodo di istanza nascondere un'estensione del metodo con lo stesso nome?sarebbe fonte di confusione codice per farlo?).

Altri motivi per non usare regolarmente questo modello?


Chiarimento:

Sì, vedo la tendenza con i metodi di estensione è quello di finire con loro ovunque.Mi piacerebbe essere particolarmente attenta avere qualsiasi .Valore netto tipi senza una grande quantità di peer review (penso l'unico che abbiamo su una stringa è un .SplitToDictionary() - simile a .Split() ma l'assunzione di un valore-chiave delimitatore di troppo)

Penso che c'è tutta una best practice dibattito c' ;-)

(Per inciso:DannySmurf, il PM suoni spaventosi.)

Specificamente sto chiedendo qui sull'utilizzo di metodi di estensione, dove in precedenza abbiamo avuto i metodi dell'interfaccia.


Sto cercando di evitare un sacco di livelli di classi base astratte - le classi di implementazione di questi modelli in gran parte già dispone di classi base.Penso che questo modello potrebbe essere più gestibile e meno eccessivamente accoppiato che l'aggiunta di ulteriori gerarchie di oggetti.

È questo ciò che MS ha fatto per IEnumerable e IQueryable per Linq?

È stato utile?

Soluzione

Penso che il sapiente utilizzo di metodi di estensione inserire le interfacce più equatable posizione (abstract) classi di base.


Il controllo delle versioni. Uno dei vantaggi di base classi hanno più di interfacce è che si può facilmente aggiungere nuovi membri virtuali in una versione successiva, mentre l'aggiunta di membri di un'interfaccia rompere gli implementatori costruito contro la vecchia versione della libreria.Invece, una nuova versione dell'interfaccia con i nuovi membri deve essere creato, e la biblioteca dovrà risolvere o limitare l'accesso a oggetti legacy solo l'attuazione, l'interfaccia originale.

Come esempio concreto, la prima versione di una libreria può definire un'interfaccia in questo modo:

public interface INode {
  INode Root { get; }
  List<INode> GetChildren( );
}

Una volta che la biblioteca ha rilasciato, non è possibile modificare l'interfaccia senza rompere gli attuali utenti.Invece, nella prossima versione ci sarebbe bisogno di definire una nuova interfaccia per aggiungere ulteriori funzionalità:

public interface IChildNode : INode {
  INode Parent { get; }
}

Tuttavia, solo gli utenti della nuova biblioteca sarà in grado di attuare la nuova interfaccia.Per lavorare con il codice legacy, abbiamo bisogno di adattare il vecchio attuazione di un metodo di estensione in grado di gestire bene:

public static class NodeExtensions {
  public INode GetParent( this INode node ) {
    // If the node implements the new interface, call it directly.
    var childNode = node as IChildNode;
    if( !object.ReferenceEquals( childNode, null ) )
      return childNode.Parent;

    // Otherwise, fall back on a default implementation.
    return FindParent( node, node.Root );
  }
}

Ora tutti gli utenti della nuova biblioteca in grado di trattare sia legacy e moderne implementazioni in modo identico.


Sovraccarichi. Un'altra area in cui i metodi di estensione può essere utile nel fornire i sovraccarichi per i metodi di interfaccia.Si potrebbe avere un metodo con diversi parametri per controllare la sua azione, di cui solo la prima o le prime due sono importanti per il 90%, caso.Dato che il C# non consente di impostare valori di default per i parametri, gli utenti di chiamare il totalemente metodo, ogni ora, o ogni realizzazione deve implementare l'banale overload del metodo del nucleo.

Invece i metodi di estensione può essere utilizzato per fornire il banale sovraccarico implementazioni:

public interface ILongMethod {
  public bool LongMethod( string s, double d, int i, object o, ... );
}

...
public static LongMethodExtensions {
  public bool LongMethod( this ILongMethod lm, string s, double d ) {
    lm.LongMethod( s, d, 0, null );
  }
  ...
}


Si prega di notare che entrambi questi casi sono scritti in termini di operazioni previste dalle interfacce, e coinvolgere banale o note di implementazioni di default.Detto questo, si può ereditare da una classe di una volta, e l'uso mirato di metodi di estensione in grado di fornire un valido modo per affrontare alcune delle sottigliezze forniti da classi di base che le interfacce mancanza :)


Edit: Un relativo post by Joe Duffy: I metodi di estensione come interfaccia di default le implementazioni del metodo

Altri suggerimenti

I metodi di estensione deve essere utilizzato come:le estensioni.Qualsiasi cruciale di struttura e di progettazione relativo codice o non banale operazione dovrebbe essere messo in un oggetto che è composto in/ereditato da una classe o interfaccia.

Una volta che un altro oggetto tenta di utilizzare esteso, non vedi le estensioni e potrebbe avere reimplementare/re-riferimento di nuovo.

La saggezza tradizionale è che i metodi di Estensione deve essere utilizzato solo per:

  • classi di utilità, come Vaibhav menzionato
  • estendendo sigillato 3rd party Api

Penso che la cosa migliore che i metodi di estensione sostituire tutte quelle classi di utilità che si trova in ogni progetto.

Almeno per ora, ho la sensazione che l'utilizzo di altri metodi di Estensione potrebbe causare confusione nel posto di lavoro.

I miei due bit.

Vedo che separa il dominio/modello e interfaccia utente e le funzionalità di visualizzazione utilizzando i metodi di estensione come una buona cosa, soprattutto dal momento che può risiedere nel separare gli spazi dei nomi.

Per esempio:

namespace Model
{
    class Person
    {
        public string Title { get; set; }
        public string FirstName { get; set; }
        public string Surname { get; set; }
    }
}

namespace View
{
    static class PersonExtensions
    {
        public static string FullName(this Model.Person p)
        {
            return p.Title + " " + p.FirstName + " " + p.Surname;
        }

        public static string FormalName(this Model.Person p)
        {
            return p.Title + " " + p.FirstName[0] + ". " + p.Surname;
        }
    }
}

In questo modo i metodi di estensione possono essere utilizzati allo stesso modo per XAML i modelli di dati.È possibile accedere private/protected membri della classe, ma permette l'astrazione dati per essere mantenuta senza eccessiva duplicazione del codice in tutta l'applicazione.

Non c'è niente di sbagliato con l'estensione interfacce, infatti, che è come LINQ opere di aggiungere l'estensione metodi per la raccolta classi.

Detto questo, si dovrebbe davvero eseguire questa operazione solo nel caso in cui è necessario fornire la stessa funzionalità in tutte le classi che implementano l'interfaccia e funzionalità che non è (e probabilmente non dovrebbe essere) parte del "ufficiale" l'attuazione di eventuali classi derivate.L'estensione di un'interfaccia è buona anche se è solo pratico per scrivere un metodo di estensione per ogni possibile tipo derivato che richiede la nuova funzionalità.

Vedo un sacco di persone che sostenendo che utilizza una classe di base per condividere le funzionalità più comuni.Prestare attenzione a questo - si dovrebbe favorire la composizione dell'ereditarietà.Eredità deve essere utilizzato solo per il polimorfismo, quando ha senso da un punto di modellazione di vista.Non è un buon strumento per il riutilizzo del codice.

Come per la domanda:Essere consapevole delle limitazioni quando si fa questo - per esempio nel codice mostrato, utilizzando un metodo di estensione per implementare GetChildren efficacemente "sigilli" di questa implementazione e non consente alcuna IHaveChildren impl a fornire la propria, se necessario.Se questo è OK, quindi non mi dispiace l'estensione del metodo di approccio che molto.Non è scolpito nella pietra, e di solito può essere facilmente refactoring quando è necessaria una maggiore flessibilità in seguito.

Per una maggiore flessibilità, utilizzando il modello di strategia può essere preferibile.Qualcosa di simile a:

public interface IHaveChildren 
{
    string ParentType { get; }
    int ParentId { get; }
}

public interface IChildIterator
{
    IEnumerable<IChild> GetChildren();
}

public void DefaultChildIterator : IChildIterator
{
    private readonly IHaveChildren _parent;

    public DefaultChildIterator(IHaveChildren parent)
    {
        _parent = parent; 
    }

    public IEnumerable<IChild> GetChildren() 
    { 
        // default child iterator impl
    }
}

public class Node : IHaveChildren, IChildIterator
{ 
    // *snip*

    public IEnumerable<IChild> GetChildren()
    {
        return new DefaultChildIterator(this).GetChildren();
    }
}

Un po ' di più.

Se interfacce multiple hanno la stessa estensione firma del metodo, si avrebbe bisogno di convertire in modo esplicito il chiamante a un tipo di interfaccia, e quindi chiamare il metodo.E. g.

((IFirst)this).AmbigousMethod()

Ouch.Si prega di non estendere le Interfacce.
L'interfaccia è pulita contratto che una classe deve implementare, e l'utilizzo di detto classi deve essere limitato a quello che è l'Interfaccia principale per funzionare correttamente.

Che è il motivo per cui è sempre dichiarare l'interfaccia come il tipo al posto dell'attuale classe.

IInterface variable = new ImplementingClass();

Giusto?

Se si ha realmente bisogno di un contratto con alcune funzionalità aggiuntive, le classi astratte sono i tuoi amici.

Rob Connery (Subsonica e MVC Storefront) implementato un IRepository-come il modello nel suo Negozio di applicazioni.Non è proprio come il modello precedente, ma condivide alcune somiglianze.

Lo strato di dati restituisce IQueryable che permette il consumo di livello per applicare il filtro e ordinamento espressione in cima a quello.Il bonus è di essere in grado di specificare un singolo metodo GetProducts, per esempio, e poi decidere in modo appropriato nel consumo di strato di come si desidera che l'ordinamento, filtraggio o anche solo di una particolare gamma di risultati.

Un approccio non tradizionale, ma molto fresco e sicuramente un caso di LAVAGGIO.

Un problema che posso vedere è che, in una società di grandi dimensioni, questo modello potrebbe permettere al codice di diventare difficile (se non impossibile) per chiunque di comprendere e utilizzare.Se più sviluppatori sono costantemente aggiungendo i propri metodi di classi esistenti, distinte da quelle classi (e, che Dio ci aiuti tutti, per BCL anche classi), ho potuto vedere una base di codice di filatura fuori controllo piuttosto rapidamente.

Anche al mio lavoro, ho potuto vedere che questo accada, con la mia PM desiderio di aggiungere ogni bit di codice che lavoriamo su di interfaccia utente o il livello di accesso ai dati, ho potuto vedere totalmente di lui insistendo su 20 o 30 metodi di essere aggiunto al Sistema.Stringa che sono solo tangenzialmente relative alla gestione delle stringhe.

Ho bisogno di risolvere qualcosa di simile:Volevo avere un Elenco<IIDable> passato per le estensioni funzione dove IIDable è un'interfaccia che ha una lunga getId() funzione.Ho provato ad utilizzare GetIds(questo Elenco<IIDable> bla), ma il compilatore non mi permettono di farlo.I modelli utilizzati, invece, e quindi di tipo colato all'interno della funzione per il tipo di interfaccia.Avevo bisogno di questa funzione per alcuni linq to sql generato classi.

Spero che questo aiuta :)

    public static List<long> GetIds<T>(this List<T> original){
        List<long> ret = new List<long>();
        if (original == null)
            return ret;

        try
        {
            foreach (T t in original)
            {
                IIDable idable = (IIDable)t;
                ret.Add(idable.getId());
            }
            return ret;
        }
        catch (Exception)
        {
            throw new Exception("Class calling this extension must implement IIDable interface");
        }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top