2010-06-28 9 views
5

Ci scusiamo per il titolo vago, ma non ero sicuro di come riassumere questo in una frase. Ho una situazione con un sacco di codice C# ridondante, e sembra davvero un qualche tipo di trucco furbo usando alcune proprietà di ereditarietà o generici che risolvono questo. Tuttavia, non sono un programmatore terribilmente esperto (in particolare con C#) e non riesco a vedere la soluzione.Problemi a liberarsi del codice ridondante via ereditarietà o generici

La situazione, in forma semplificata, è simile a questa. Ho un sacco di classi che ereditano tutte da un tipo.

public class Foo : SuperFoo 
{ 
    ... 
    public Foo SomeMethod() { ... } 
} 
public class Bar : SuperFoo 
{ 
    ... 
    public Bar SomeMethod() { ... } 
} 
public class Baz : SuperFoo 
{ 
    ... 
    public Baz SomeMethod() { ... } 
} 
...  
public class SuperFoo 
{ 
    ... 
} 

Il problema si presenta quando è necessario elaborare raccolte di questi oggetti. La mia soluzione prima bozza (quella cattiva) è la seguente:

public void SomeEventHasHappened(...) 
{ 
    ProcessFoos(); 
    ProcessBars(); 
    ProcessBazes(); 
    ... 
} 

public void ProcessFoos() 
{ 
    ... 
    foreach (var foo in fooList) 
    { 
      ... 
      foo.SomeMethod(); 
    } 
} 
public void ProcessBars() 
{ 
    ... 
    foreach (var bar in barList) 
    { 
      ... 
      bar.SomeMethod(); 
    } 
} 

... e così via. Il problema è che fondamentalmente tutto il codice nei metodi ProcessX è lo stesso, a parte il tipo di oggetti su cui si sta operando. Sarebbe bello consolidare tutti questi in un unico metodo per ovvi motivi.

Il mio primo pensiero è stato semplicemente creare un metodo Process() generico che prendesse un parametro List<SuperFoo> come parametro e procedesse da lì. Il problema è che un SuperFoo generico non ha un SomeMethod(), e non può averne uno perché ognuna delle classi figlie 'SomeMethod() ha un tipo di ritorno diverso, quindi avere delle sostituzioni non funziona.

+0

Ho visto il codice in cui viene restituito un 'oggetto' e quindi eseguito il cast nel tipo appropriato. –

+0

Anche se questo risolvesse il problema come l'ho presentato qui, dovrei chiarire ... Preferirei non avere 'SomeMethod()' restituisce un generale 'object', perché viene anche chiamato' SomeMethod() ' molti altri posti oltre a questa parte del codice e che richiederebbero il casting in tutti questi altri posti. Grazie per il suggerimento, però. – jloubert

risposta

2

Di solito aggiungo un'interfaccia che funziona su tipi di base.

interface ISuperFoo 
{ 
    public ISuperFoo SomeMethod() { ... } 
} 

public class Foo : SuperFoo, ISuperFoo 
{ 
    // concrete implementation 
    public Foo SomeMethod() { ... } 

    // method for generic use, call by base type 
    public ISuperFoo ISuperFoo.SomeMethod() 
    { 
     return SomeMethod(); 
    } 
} 

public void Processs() 
{ 
    ... 
    foreach (var iSuperFoo in list) 
    { 
      ... 
      iSuperFoo.SomeMethod(); 
    } 
} 

Ovviamente dipende da cosa si sta utilizzando il risultato.

A volte è possibile effettuare il utilizzando i farmaci generici, ma è anche possibile finire in disordine. A volte è solo più facile da downcast da qualche parte. Certo, cerchi di evitarlo ogni volta che te lo puoi permettere.

+0

Questo è abbastanza bello, e probabilmente qualcosa a cui avrei dovuto pensare! Grazie per la risposta. – jloubert

2

Ecco un esempio di come questo potrebbe funzionare utilizzando i generici e rendere SuperFoo una classe astratta.

public interface ISuperFoo 
{ 
    ... 
} 

public abstract class SuperFoo<T> where T : ISuperFoo 
{ 
    public abstract T SomeMethod(); 
} 

public class BazReturn : ISuperFoo 
{ 
    ... 
} 

public class Baz: SuperFoo<BazReturn> 
{ 
    public override BazReturn SomeMethod() 
    { 
     throw new NotImplementedException(); 
    } 
} 

public class BarReturn : ISuperFoo 
{ 
    ... 
} 

public class Bar : SuperFoo<BarReturn> 
{ 
    public override BarReturn SomeMethod() 
    { 
     throw new NotImplementedException(); 
    } 
} 

public static class EventHandler 
{ 
    public static void SomeEventHasHappened(List<SuperFoo<ISuperFoo>> list) 
    { 
     foreach (SuperFoo<ISuperFoo> item in list) 
     { 
      ISuperFoo result = item.SomeMethod(); 
     } 
    } 
} 

si potrebbe sostituire l'interfaccia ISuperFoo con una classe concreta, se necessario, ma poi si dovrà lanciare il valore di ritorno, che tipo di sconfitte lo scopo.

public abstract class SuperFoo<T> 
{ 
    public abstract T SomeMethod(); 
} 

public class Foo : SuperFoo<int> 
{ 
    public override int SomeMethod() 
    { 
     throw new NotImplementedException(); 
    } 
} 

public static class EventHandler 
{ 
    public static void SomeEventHasHappened(List<SuperFoo<int>> list) 
    { 
     foreach (SuperFoo<int> item in list) 
     { 
      item.SomeMethod(); 
     } 
    } 
} 
+0

Grazie per la risposta. Ma sono un po 'confuso dalle classi FooReturn e Foo nella prima parte della tua risposta. Se lo capisco correttamente, sembra che un'istanza di Foo's SomeMethod() restituisca un oggetto FooReturn invece di un oggetto Foo. Quindi, tutti gli altri codici che erano in Foo ora dovrebbero essere in FooReturn, mentre Foo deve solo contenere il metodo SomeMethod(). Se questo è davvero il caso, non sarebbe questo codice di interruzione in qualche altro posto che ha 'Foo fooObject = anotherFooObject.SomeMethod();'? – jloubert

+0

Il tipo di ritorno di SomeMethod dovrebbe sempre essere considerato come restituire ISuperFoo. Noterai che la generica varialba T astratta del SuperFoo deve estendere ISuperFoo.Questo è ciò che dà il tipo di ritorno coerente richiesto. Ho aggiunto un esempio di barra al mio codice che, si spera, chiarirò. – sgriffinusa

+0

Ho anche ribattezzato la classe figlio da Foo a Baz, che si spera possa ulteriormente chiarire la differenza tra l'interfaccia, l'abstract e i tipi di estensione. – sgriffinusa