Suggerimenti desiderati con elenchi o enumeratori di T quando si eredita da classi generiche

StackOverflow https://stackoverflow.com/questions/53395

  •  09-06-2019
  •  | 
  •  

Domanda

So che la risposta non sarà semplice e utilizzo già un paio di cludge (credo brutti).Sto semplicemente cercando alcune risposte eleganti.

Classe astratta:

public interface IOtherObjects;

public abstract class MyObjects<T> where T : IOtherObjects
{
   ...

   public List<T> ToList()
   {
       ...
   }
}

Bambini:

public class MyObjectsA : MyObjects<OtherObjectA> //(where OtherObjectA implements IOtherObjects)
{


}

public class MyObjectsB : MyObjects<OtherObjectB> //(where OtherObjectB implements IOtherObjects)
{


}

È possibile scorrere una raccolta di MyObjects (o altri raggruppamenti simili, generici o meno) per poi utilizzarli Elencare metodo del I miei oggetti classe base, poiché a questo punto non conosciamo specificatamente il tipo di T.

MODIFICAREPer quanto riguarda esempi specifici, ogni volta che si è verificato questo problema, ci ho pensato per un po' e ho invece fatto qualcosa di diverso, quindi non esiste alcun requisito attuale.ma poiché si verifica abbastanza spesso, ho pensato di farlo galleggiare.

MODIFICARE@Sara, non è il tipo specifico della raccolta che mi interessa, potrebbe essere una Lista, ma comunque il metodo ToList di ogni istanza è relativamente inutilizzabile, senza un tipo anonimo)

@aku, vero, e questa domanda può essere relativamente ipotetica, tuttavia essere in grado di recuperare e lavorare con un elenco di T di oggetti, conoscendo solo il loro tipo di base sarebbe molto utile.Avere ToList che restituisce un List Of BaseType è stata una delle mie soluzioni alternative

MODIFICARE @ Tutto:Finora questo è stato il tipo di discussione che speravo, anche se conferma ampiamente tutto ciò che sospettavo.Grazie a tutti per ora, ma chiunque altro si senta libero di intervenire.

MODIFICARE@Rob, Sì, funziona per un tipo definito, ma non quando il tipo è noto solo come Elenco di IOtherObjects.

@Rapinare Ancora Grazie.Di solito questa è stata la mia complicata soluzione alternativa (senza mancanza di rispetto :)).Oppure utilizzare la funzione ConvertAll per eseguire il downcast tramite un delegato.Grazie per aver dedicato del tempo per comprendere il problema.

MODIFICA DI QUALIFICAZIONE nel caso fossi stato un po' confuso

Per essere più precisi, (potrei aver lasciato che la mia ultima implementazione diventasse troppo complessa):

diciamo che ho 2 tipi di oggetto, B e C che ereditano dall'oggetto A.

Si sono presentati molti scenari in cui, da un Elenco di B o un Elenco di C, o in altri casi un Elenco di entrambi - ma non so quale se mi trovo in una classe base, ho avuto bisogno di un Elenco di UN.

L'esempio sopra era un esempio annacquato del Elenco dei meno specifici l'ultima incarnazione del problema.

Di solito si presenta, come penso, attraverso possibili scenari che limitano la quantità di codice da scrivere e sembra un po' più elegante rispetto ad altre opzioni.Volevo davvero una discussione sulle possibilità e su altri punti di vista, che più o meno ho ottenuto.Sono sorpreso che nessuno abbia menzionato ConvertAll() finora, poiché è un'altra soluzione alternativa che ho usato, ma un po' troppo dettagliata per gli scenari in questione

@Rapinare Ancora una volta e Sara

Grazie, tuttavia sento di comprendere i farmaci generici in tutta la loro gloria statica e contestualizzata e ho compreso i problemi in gioco qui.

La progettazione effettiva del nostro sistema e l'utilizzo dei farmaci generici (e posso dirlo senza solo un pizzico di pregiudizio, poiché ero solo uno dei giocatori nella progettazione), sono stati fatti bene.È quando ho lavorato con l'API principale, che ho trovato situazioni in cui mi trovavo nell'ambito sbagliato per fare qualcosa in modo semplice, invece ho dovuto gestirle in modo un po' meno elegante di quanto mi piace (cercando di essere intelligente o forse pigro: accetterò entrambe le etichette).

Il mio disgusto per quello che ho definito un errore è in gran parte dovuto al fatto che dobbiamo fare un ciclo attraverso il nostro set di record semplicemente per convertire gli oggetti nel loro valore di base, il che potrebbe essere un calo delle prestazioni.

Immagino che mi stavo chiedendo se qualcun altro si fosse imbattuto prima in questo nella loro codifica, e se qualcuno fosse stato più intelligente, o almeno più elegante, di me nell'affrontarlo.

È stato utile?

Soluzione

Se hai

class B : A
class C : A

E tu hai

List<B> listB;
List<C> listC;

che desideri trattare come un elenco del tipo genitore

Allora dovresti usare

List<A> listA = listB.Cast<A>().Concat(listC.Cast<A>()).ToList()

Altri suggerimenti

perché hai una raccolta di MyObjects?C'è un motivo specifico per cui non hai una Lista?

Nel tuo caso MyObjectsA e MyObjectsB non hanno un predecessore comune.La classe generica è il modello per diverso classi non una classe base comune.Se vuoi avere proprietà comuni in classi diverse usa le interfacce.Non puoi chiamare Elencare in un ciclo perché ha una firma diversa in classi diverse.Puoi creare ToList che restituisce oggetti piuttosto che di tipo specifico.

Probabilmente puoi ancora accedere al metodo ToList(), ma poiché non sei sicuro del tipo, non funzionerà?

foreach(var myObject in myObjectsList)
    foreach(var obj in myObject.ToList())
        //do something

Ovviamente funzionerà solo su C# 3.0.

Si noti che l'uso di var serve semplicemente a rimuovere la necessità di sapere quale tipo contengono le liste;al contrario dei commenti di Frank secondo cui ho l'illusione che var renda dinamica la digitazione.

OK, sono confuso, il seguente codice funziona bene per me (la curiosità ha avuto la meglio su di me!):

// Original Code Snipped for Brevity - See Edit History if Req'd

Oppure mi sono perso qualcosa?

Aggiornamento in seguito alla risposta dell'OP

OK ora sono davvero confuso..Quello che stai dicendo è che vuoi ottenere un elenco di Digitato valori da un elenco generico/astratto?(le classi figlie diventano quindi irrilevanti).

Non è possibile restituire un elenco tipizzato se i tipi sono figli/implementatori dell'interfaccia: non corrispondono!Ovviamente puoi ottenere un elenco di elementi di un tipo specifico dall'elenco astratto in questo modo:

    public List<OfType> TypedList<OfType>() where OfType : IOtherObjects
    {
        List<OfType> rtn = new List<OfType>();

        foreach (IOtherObjects o in _objects)
        {
            Type objType = o.GetType();
            Type reqType = typeof(OfType);

            if (objType == reqType)
                rtn.Add((OfType)o);
        }

        return rtn;
    }

Se sono ancora fuori base qui, puoi per favore riformulare la tua domanda?!(Non sembra che io sia l'unico a non essere sicuro di cosa stai guidando).Sto cercando di stabilire se da parte tua c'è un malinteso sui farmaci generici.

Un altro aggiornamento: D

Giusto, quindi sembra che tu voglia/abbia bisogno dell'opzione per ottenere l'elenco digitato o l'elenco di base, sì?

Ciò renderebbe la tua classe astratta simile a questa: puoi utilizzare ToList per ottenere il tipo concreto o ToBaseList() per ottenere un elenco del tipo di interfaccia.Questo dovrebbe funzionare in tutti gli scenari che hai.Questo aiuta?

public abstract class MyObjects<T> where T : IOtherObjects
{
    List<T> _objects = new List<T>();

    public List<T> ToList()
    {
        return _objects;
    }

    public List<IOtherObjects> ToBaseList()
    {
        List<IOtherObjects> rtn = new List<IOtherObjects>();
        foreach (IOtherObjects o in _objects)
        {
            rtn.Add(o);
        }
        return rtn;
    }
}

Aggiornamento n.3

Non è proprio una soluzione "complicata" (senza mancare di rispetto): è l'unico modo per farlo..Penso che il problema più grande qui sia un problema di progettazione/grok.Hai detto di avere un problema, questo codice lo risolve.Ma se ti aspettavi di fare qualcosa del tipo:

public abstract class MyObjects<T> where T : IOtherObjects
{
    List<T> _objects = new List<T>();

    public List<IOtherObjects> Objects
    { get { return _objects; } }
}
#warning This won't compile, its for demo's sake.

Ed essere in grado di scegliere i tipi che ne derivano, in che altro modo Potevo lo fai?!Ho la sensazione che tu non capisca veramente quale sia lo scopo dei farmaci generici e stai cercando di convincerli a fare qualcosa per cui non sono progettati!?

Recentemente ho trovato il

List<A>.Cast<B>().ToList<B>()

modello.

Fa esattamente quello che cercavo,

I generici vengono utilizzati per controlli di tipo temporale statico non invio in fase di esecuzione.Utilizzare ereditarietà/interfacce per l'invio in fase di esecuzione, utilizzare generici per garanzie di tipo in fase di compilazione.

interface IMyObjects : IEnumerable<IOtherObjects> {}
abstract class MyObjects<T> : IMyObjects where T : IOtherObjects {}

IEnumerable<IMyObjects> objs = ...;
foreach (IMyObjects mo in objs) {
    foreach (IOtherObjects oo in mo) {
        Console.WriteLine(oo);
    }
}

(Ovviamente preferisco gli Enumerable alle Liste.)

O Basta usare un linguaggio dinamico adeguato come VB.:-)

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