Funzione generica in un elenco di classi derivate
Domanda
Sento che la mia domanda è piuttosto stupida, o un altro modo per dirla è: sono troppo perso nel mio codice per vedere una soluzione per ora. Rimani troppo a lungo su un problema e la tua visione diventa sempre più stretta & Gt; & Lt ;. Inoltre non sono abbastanza bravo con l'eredità, il polimorfismo e così via
Ecco l'idea: ho un elenco multiplo di classe derivata e vorrei chiamare funzioni generiche su quegli elenchi (accesso e modifica dei membri della classe base). Sento che c'è qualcosa a che fare con l'eredità, ma non riesco a farlo funzionare come voglio per ora.
Ecco un esempio molto semplice di ciò che intendo fare:
class Baseclass
{
public int ID;
public string Name;
}
class DerivedClass1 : Baseclass
{
}
private void FuncOnBase(List<Baseclass> _collection)
{
// ...
foreach (Baseclass obj in _collection)
{
++obj.ID;
}
// ...
}
private void FuncTest()
{
List<DerivedClass1> collection1 = new List<DerivedClass1>();
collection1.Add(new DerivedClass1() { ID = 1 });
collection1.Add(new DerivedClass1() { ID = 2 });
collection1.Add(new DerivedClass1() { ID = 3 });
FuncOnBase(collection1); // ==> forbidden, cannot convert the derived class list to the base class list
}
Soluzione
Devo amare la varianza. Un List<DerivedClass1>
non è non un List<Baseclass>
- altrimenti, FuncOnBase
potrebbe tentare di aggiungere un Baseclass
all'elenco e il compilatore non lo individuerebbe.
Un trucco è usare un metodo generico:
private void FuncOnBase<T>(List<T> _collection) where T : Baseclass
{
// ...
foreach (T obj in _collection)
{
obj.ID++;
}
// ...
}
In termini dell'esempio che ho presentato sopra, nota che siamo in grado di aggiungere un T
all'elenco; utile in particolare se aggiungiamo il T : new()
vincolo o passiamo (ad esempio) a params T[]
.
Nota anche che IEnumerable<T>
diventa covariante in C # 4.0 / .NET 4.0, quindi se passi solo un IEnumerable<Baseclass>
(piuttosto che un elenco) funzionerebbe " come " :
private void FuncOnBase(IEnumerable<Baseclass> _collection)
{
///...
}
Altri suggerimenti
Se stai solo eseguendo un foreach
, dichiara FuncOnBase(IEnumerable<Baseclass> collection)
, che puoi chiamare da FuncTest
in questo modo:
FuncOnBase(collection1.Cast<Baseclass>());
Quando dichiari un metodo con un parametro List<T>
ma usi solo le sue IEnumerable<T>
caratteristiche, stai aggiungendo vincoli API che non significano nulla nel tuo codice.