subarray - tolist c#




Perché non posso usare l'enumeratore di un array, invece di implementarlo da solo? (2)

Ho un codice come questo:

public class EffectValues : IEnumerable<object>
{
    public object [ ] Values { get; set; }

    public IEnumerator<object> GetEnumerator ( )
    {
        return this.Values.GetEnumerator ( );
    }

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

Ma il compilatore si lamenta dicendo:

"Impossibile convertire implicitamente il tipo 'System.Collections.IEnumerator' in 'System.Collections.Generic.IEnumerator'. Esiste una conversione esplicita (ti manca un cast?)"

Pensavo che il tipo Array implementasse entrambe le interfacce IEnumerable, non è così? Perché posso utilizzare direttamente le funzionalità di Linq nell'istanza Values.


È necessario eseguire il cast della matrice su IEnumerable<object> per poter accedere all'enumeratore generico:

public IEnumerator<object> GetEnumerator() {
  return ((IEnumerable<object>)this.Values).GetEnumerator();
}

Questo è un sottile e un po 'sfortunato. La soluzione facile è:

public IEnumerator<object> GetEnumerator ( )
{
     return ((IEnumerable<object>)this.Values).GetEnumerator ( );     
} 

Pensavo che il tipo Array implementasse entrambe le interfacce IEnumerable, non è così?

Le regole sono:

  • System.Array implementa IEnumerable "implicitamente", con metodi pubblici.
  • ogni tipo di array T [] eredita da System.Array.
  • ogni tipo di array T [] implementa IList<T> , IEnumerable<T> e così via.
  • quindi ogni tipo di matrice T [] è convertibile in IEnumerable<T>

Si noti che il terzo punto NON era

  • ogni tipo di array T [] implementa IList<T> , IEnumerable<T> e così via con metodi e proprietà pubbliche definiti su T [] che implementano implicitamente i membri

E tu ci vai. Quando cerchi GetEnumerator, lo cerchiamo su object [] e non lo trovi, perché object [] implementa IEnumerable<object> esplicitamente . È convertibile in IEnumerable<object> e la convertibilità non conta per le ricerche. (Non ti aspetteresti che un metodo di "double" appaia su int solo perché int è convertibile in double.) Quindi guardiamo il tipo di base e scopriamo che System.Array implementa IEnumerable con un metodo pubblico, quindi abbiamo trovato il nostro GetEnumerator.

Cioè, pensaci in questo modo:

namespace System
{
    abstract class Array : IEnumerable
    {
        public IEnumerator GetEnumerator() { ... }
        ...
    }
}

class object[] : System.Array, IList<object>, IEnumerable<object>
{
    IEnumerator<object> IEnumerable<object>.GetEnumerator() { ... }
    int IList<object>.Count { get { ... } }
    ...
}

Quando chiamate GetEnumerator su object [], non vediamo l'implementazione che è un'implementazione esplicita dell'interfaccia, quindi andiamo alla classe base, che ne ha una visibile.

In che modo tutte le classi [], int [], string [], SomeType [] vengono generate "al volo"?

Magia!

Questo non è generici, giusto?

Destra. Gli array sono tipi molto speciali e sono cotti a livello profondo nel sistema di tipo CLR. Sebbene siano molto simili ai farmaci generici in molti modi.

Sembra questo class object [] : System.Array è qualcosa che non può essere implementato da un utente, giusto?

Giusto, questo era solo per illustrare come pensarci.

Quale pensi sia meglio: lanciare il GetEnumerator() su IEnumerable<object> , o semplicemente usare foreach e yield ?

La domanda è mal formata. Non si esegue il cast del GetEnumerator su IEnumerable<object> . È possibile eseguire il cast della matrice su IEnumerable<object> oppure eseguire il cast di GetEnumerator su IEnumerator<object> .

Probabilmente scriverò Valori su IEnumerable<object> e richiamerò GetEnumerator su di esso.

Probabilmente userò casting, ma mi chiedo se questo è un posto in cui tu o un programmatore che potrebbero leggere il codice, penserebbero che sia meno chiaro.

Penso che sia abbastanza chiaro con il cast.

quando hai detto implementazione implicita, intendi nella forma di Interface.Method, giusto?

No, il contrario:

interface IFoo { void One(); void Two(); }
class C : IFoo
{
    public void One() {} // implicitly implements IFoo.One
    void IFoo.Two() {} // explicitly implements IFoo.Two
}

La prima dichiarazione implementa silenziosamente il metodo. Il secondo è esplicito su quale metodo di interfaccia implementa.

Qual è la ragione per l'implementazione di IEnumerable<T> come tale, invece dell'implementazione implicita con metodi pubblici? Mi sono incuriosito perché hai detto "Questo è un po 'sfortunato", quindi sembra che sia a causa di una vecchia decisione che ti ha costretto a farlo immagino?

Non so chi abbia preso questa decisione. È un po 'sfortunato però. È confuso almeno un utente - tu - e mi ha confuso per qualche minuto anche lì!

Avrei pensato che il tipo di array fosse qualcosa del genere: public class Array<T> : IEnumerable<T> etc. Ma invece c'è un codice magico su di esso, giusto?

Destra. Come hai notato nella tua domanda di ieri, le cose sarebbero state molto diverse se avessimo avuto generici in CLR v1.

Gli array sono essenzialmente un tipo di raccolta generico. Poiché sono stati creati in un sistema di tipi che non ha generici, ci deve essere un sacco di codice speciale nel sistema di tipi per gestirli.

La prossima volta che crei un sistema di tipi, metti generici in v1 e assicurati di ottenere forti tipi di raccolta, tipi annullibili e tipi non annulli introdotti nel framework sin dall'inizio. L'aggiunta di generici e tipi di valori nullable post hoc era difficile.





ienumerable