2010-06-28 10 views
5

Sorry für die vagen Titel, aber ich war nicht sicher, wie dies in einem Satz zusammenfassen. Ich habe eine Situation mit viel redundantem C# -Code, und es sieht wirklich so aus, als würde eine Art verrückter Trick, der eine Eigenschaft der Vererbung oder Generika verwendet, dies lösen. Allerdings bin ich kein besonders erfahrener Programmierer (besonders bei C#) und kann die Lösung nicht wirklich sehen.Schwierigkeiten haben, über Vererbung oder Generika von redundantem Code befreit

Die Situation, in vereinfachter Form, sieht wie folgt aus etwas. Ich habe eine Reihe von Klassen, die alle von einem Typ erben.

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

Das Problem tritt auf, wenn Sammlungen dieser Objekte verarbeitet werden müssen. Meine erste Lösung (die schlechte) sieht so aus:

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(); 
    } 
} 

... und so weiter. Das Problem besteht darin, dass im Prinzip der gesamte Code in den ProcessX-Methoden derselbe ist wie der Typ der Objekte, die bearbeitet werden. Es wäre schön, all dies aus offensichtlichen Gründen in einer Methode zusammenzufassen.

Mein erster Gedanke war, nur eine generische Process() -Methode zu machen, die einen List<SuperFoo> als Parameter nahm und nur von dort fortfahren. Das Problem ist, dass ein generisches SuperFoo keinen Somemethod hat(), und es kann man nicht haben, weil jeder der Somemethod Kinderklassen() einen anderen Rückgabetyp hat, so überschreibt, die nicht funktioniert.

+0

Ich habe Code gesehen, wo ein 'Objekt' zurückgegeben wird, und dann in den entsprechenden Typ umgewandelt. –

+0

Während dies das Problem lösen würde, wie ich es hier vorgestellt habe, sollte ich klarstellen ... Ich würde es vorziehen, wenn 'SomeMethod()' kein allgemeines 'Objekt' zurückgibt, weil' SomeMethod() 'ebenfalls aufgerufen wird viele andere Orte neben diesem Teil des Codes, und das würde Casting in all diesen anderen Orten erfordern. Danke für den Vorschlag. – jloubert

Antwort

2

ich hinzufügen, in der Regel eine Schnittstelle, die auf Basistypen arbeitet.

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(); 
    } 
} 

Natürlich hängt es davon ab, wofür Sie das Ergebnis verwenden.

Manchmal können Sie können machen es einfacher mit Generika, aber Sie können auch in einem Durcheinander enden. Manchmal ist es einfach einfacher irgendwo runter zu gehen. Natürlich versuchen Sie dies zu vermeiden, wann immer Sie es sich leisten können.

+0

Das ist ziemlich nett und wahrscheinlich etwas, woran ich hätte denken sollen! Danke für die Antwort. – jloubert

2

Hier ist ein Beispiel dafür, wie diese könnten Generika arbeiten mit und machen SuperFoo eine abstrakte Klasse.

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(); 
     } 
    } 
} 

Sie konnten die ISuperFoo Schnittstelle mit einer konkreten Klasse ersetzen, wenn nötig, aber dann müsste man den Rückgabewert werfen, welche Art von Niederlagen der Zweck.

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

Danke für die Antwort. Aber ich bin ein wenig verwirrt von den FooReturn und Foo Klassen im ersten Teil Ihrer Antwort. Wenn ich das richtig verstehe, sieht es so aus, als würde eine Instanz von Foo's SomeMethod() ein FooReturn-Objekt anstelle eines Foo-Objekts zurückgeben. Daher sollte der gesamte Code, der früher in Foo war, jetzt in FooReturn sein, während Foo nur SomeMethod() enthalten muss. Wenn dies tatsächlich der Fall ist, würde in einem anderen Ort nicht diesen Bruch-Code, der 'Foo fooObject = anotherFooObject.SomeMethod() hat;'? – jloubert

+0

Der Rückgabetyp von SomeMethod sollte immer als Rückgabe von ISuperFoo behandelt werden. Sie werden bemerken, dass die generische Variable T des abstrakten SuperFoo ISuperFoo erweitern muss.Dies gibt den konsistenten Rückgabetyp, der erforderlich ist. Ich habe meinem Code ein Bar-Beispiel hinzugefügt, das hoffentlich klären wird. – sgriffinusa

+0

Ich habe auch die Kind-Klasse von Foo zu Baz umbenannt, die hoffentlich den Unterschied zwischen den Interfaces, den abstrakten und den extendierenden Typen weiter verdeutlichen wird. – sgriffinusa