Domanda

Aggiorna : Apprezzo tutti i commenti, che sono in sostanza comprese unanime opposizione. Mentre ogni obiezione era valido, ritengo che il chiodo finale nella bara era condizioni astuta di Ani che, in ultima analisi, anche il una minuscolo beneficio che questa idea apparentemente offerto - l'eliminazione del boilerplate codice -. è stato negato dal fatto che l'idea stessa richiederebbe la sua proprio codice standard

Quindi sì, in considerazione mi ha convinto:. Sarebbe una cattiva idea

E proprio al tipo di salvataggio mia dignità un po ': forse avrei giocato per amor di discussione, ma non sono mai stato realmente venduti su questa idea per cominciare - semplicemente curioso di sentire ciò che gli altri hanno da dire in proposito. Onesto.


Prima di respingere questa domanda assurda, vi chiedo di prendere in considerazione quanto segue:

  1. eredita IEnumerable<T> da * IEnumerable, che significa che qualsiasi tipo che implementa IEnumerable<T> generalmente deve implementare sia IEnumerable<T>.GetEnumerator e (esplicitamente) IEnumerable.GetEnumerator. Ciò equivale in sostanza a codice standard.
  2. È possibile foreach su qualsiasi tipo che ha un metodo GetEnumerator, fintanto che il metodo restituisce un oggetto di un certo tipo con un metodo e una proprietà MoveNext Current. Quindi, se il tipo definisce una con il metodo public IEnumerator<T> GetEnumerator() firma, è legale enumerare su di esso utilizzando foreach.
  3. Chiaramente, c'è un sacco di codice là fuori che richiede l'interfaccia IEnumerable<T> - per esempio, in pratica tutti i metodi di estensione LINQ. Per fortuna, per passare da un tipo che si può foreach a un IEnumerable<T> è banale mediante la generazione automatica iteratore che C # forniture tramite la parola chiave yield.

Quindi, mettendo insieme tutto questo, ho avuto questa pazza idea: e se ho appena definisco la mia propria interfaccia che assomiglia a questo:

public interface IForEachable<T>
{
    IEnumerator<T> GetEnumerator();
}

Poi ogni volta che mi definisco un tipo che voglio essere enumerabile, implemento questo interfaccia invece di IEnumerable<T>, eliminando la necessità di implementare due metodi GetEnumerator (uno esplicito) . Ad esempio:

class NaturalNumbers : IForEachable<int>
{
   public IEnumerator<int> GetEnumerator()
   {
       int i = 1;
       while (i < int.MaxValue)
       {
           yield return (i++);
       }
   }

   // Notice how I don't have to define a method like
   // IEnumerator IEnumerable.GetEnumerator().
}

Infine , al fine di rendere questo tipo compatibile con il codice che fa si aspettano l'interfaccia IEnumerable<T>, posso solo definire un metodo di estensione per andare da qualsiasi IForEachable<T> a un IEnumerable<T> in questo modo:

public static class ForEachableExtensions
{
    public static IEnumerable<T> AsEnumerable<T>(this IForEachable<T> source)
    {
        foreach (T item in source)
        {
            yield return item;
        }
    }
}

Mi sembra che facendo questo mi permette di progettare i tipi che sono utilizzabili in ogni modo come implementazioni di IEnumerable<T>, ma senza che l'attuazione IEnumerable.GetEnumerator esplicita fastidiosi in ciascuno di essi.

Ad esempio:

var numbers = new NaturalNumbers();

// I can foreach myself...
foreach (int x in numbers)
{
    if (x > 100)
        break;

    if (x % 2 != 0)
        continue;

    Console.WriteLine(x);
}

// Or I can treat this object as an IEnumerable<T> implementation
// if I want to...
var evenNumbers = from x in numbers.AsEnumerable()
                  where x % 2 == 0
                  select x;

foreach (int x in evenNumbers.TakeWhile(i => i <= 100))
{
    Console.WriteLine(x);
}

Che ne pensate voi ragazzi di questa idea? Mi sto perdendo qualche motivo per cui questo sarebbe un errore?

Mi rendo conto che probabilmente sembra una soluzione troppo complessa per ciò che non quello grande di un accordo per iniziare è (dubito chiunque davvero cure che molto di dover definire esplicitamente l'interfaccia IEnumerable) ; ma appena spuntato nella mia testa e non sto vedendo alcun problema evidente che questo approccio porrebbe.

In generale, se posso scrivere una moderata quantità di codice una volta per salvare me stesso la fatica di dover scrivere una piccola quantità di codice un sacco di volte , per me , ne vale la pena.

* è che la terminologia diritto di utilizzo? Sono sempre riluttanti a dire un'interfaccia "eredita da" un altro, in quanto questo non sembra di cogliere correttamente il rapporto tra di loro. Ma forse è proprio su.

È stato utile?

Soluzione

Non sei solo spostando il testo standard da qualche altra parte - dalla scrittura della IEnumerable.GetEnumeratormethod su ogni classe per chiamare il proprio interno AsEnumerable ogni volta che si prevede un IEnumerable<T>? In genere, mi sarei aspettato un tipo enumerabile da utilizzare per l'interrogazione di gran lunga più volte di quanto è scritto (che è esattamente una volta). Ciò significa che questo modello porterà a più boilerplate, in media.

Altri suggerimenti

Ti manca una cosa enorme -

Se si implementa la propria interfaccia, invece di IEnumerable<T>, la classe non funzionerà con i metodi quadro aspettano IEnumerable<T> - principalmente, si sarà completamente in grado di utilizzare LINQ, o utilizzare la classe per costruire un List<T>, o di molte altre astrazioni utili .

È possibile raggiungere questo obiettivo, come si parla, attraverso un metodo di estensione separata - tuttavia, questo ha un costo. Utilizzando un metodo di estensione per convertire a un IEnumerable<T>, si aggiunge un ulteriore livello di astrazione necessario per utilizzare la classe (che si farà molto più spesso di quanto la creazione della classe), e ridurre le prestazioni (il vostro metodo di estensione sarà , in effetti, generare una nuova implementazione della classe internamente, che è davvero inutile). Ancora più importante, qualsiasi altro utente della classe (o più tardi) dovrà imparare una nuova API che compie nulla - si sta facendo la classe più difficile da usare, non utilizzando interfacce standard, in quanto viola le aspettative degli utenti

Hai ragione:. It non sembra una soluzione troppo complessa per un problema abbastanza facile

Si introduce inoltre un ulteriore livello di indirezione per ogni passo dell'iterazione. Probabilmente , non un problema di prestazioni, ma ancora un po 'inutile, quando non credo che siete veramente guadagnando nulla di molto significativo.

Inoltre, anche se il vostro metodo di estensione consente di convertire qualsiasi IForEachable<T> in un IEnumerable<T>, significa che il tipo stesso non sarà di soddisfare un vincolo generico come questo:

public void Foo<T>(T collection) where T : IEnumerable

o simili. In sostanza, dal dover effettuare la vostra conversione, si sta perdendo la capacità di trattare un singolo oggetto come sia un'implementazione IEnumerable<T> e il vero tipo concreto.

Inoltre, non attuando IEnumerable, si sta contando te di inizializzatori raccolta. (A volte mi attuare IEnumerable esplicitamente solo di optare per l'inizializzazione di raccolta, un'eccezione da GetEnumerator().)

Oh, e hai anche introdotto un pizzico di infrastrutture che è sconosciuto a tutti gli altri nel mondo, rispetto alle vaste orde di C # sviluppatori che già conoscono IEnumerable<T>.

Nel nostro codice di base c'è probabilmente più di 100 istanze di questo frammento esatto:

IEnumerator IEnumerable.GetEnumerator()
{
    return this.GetEnumerator();
}

E sono davvero d'accordo con questo. E 'un piccolo prezzo da pagare per la piena compatibilità con ogni altro pezzo di codice .NET mai scritto;)

La soluzione proposta richiede essenzialmente ogni consumatore / visitatore della vostra nuova interfaccia per ricordare anche per chiamare un metodo di estensione speciale su di esso prima che sia utile .

E 'molto più facile da usare IEnumerable di riflessione. Cercando di richiamare interfacce generiche attraverso la riflessione è tale dolore a. La pena della boxe attraverso IEnumerable si perde per il sovraccarico di riflessione per sé quindi perché preoccuparsi utilizzando un'interfaccia generica? Per quanto riguarda, ad esempio, la serializzazione viene in mente.

Credo che ognuno di noi ha già menzionato le ragioni tecniche per non fare questo, quindi mi aggiungi a nel mix: che richiede l'utente a chiamare AsEnumerable () sulla vostra collezione di essere in grado di utilizzare le estensioni enumerabili violerebbe il principio di minima sorpresa.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top