2010-06-28 9 views
5

Disculpe el título impreciso, pero no estaba seguro de cómo resumir esto en una frase. Tengo una situación con mucho código C# redundante, y realmente parece que algún tipo de truco astuto usando alguna propiedad de herencia o genéricos resolvería esto. Sin embargo, no soy un programador terriblemente experimentado (particularmente con C#) y simplemente no puedo ver la solución.Tiene problemas para deshacerse del código redundante a través de la herencia o genéricos

La situación, en forma simplificada, se parece a esto. Tengo un montón de clases que todas heredan de 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 
{ 
    ... 
} 

El problema surge cuando las colecciones de estos objetos deben procesarse. Mi solución de primer borrador (la mala) se ve así:

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

... y así sucesivamente. El problema es que básicamente todo el código en los métodos ProcessX es el mismo, aparte del tipo de los objetos que se están operando. Sería bueno consolidar todos estos en un método por razones obvias.

Mi primer pensamiento fue simplemente hacer un método genérico de Process() que tomó un List<SuperFoo> como parámetro y continuar desde allí. El problema es que un SuperFoo genérico no tiene un SomeMethod(), y no puede tener uno porque cada una de las clases secundarias 'SomeMethod() tiene un tipo de devolución diferente, por lo que no funciona el reemplazo.

+0

He visto el código donde se devuelve un 'objeto', y luego se lo transfiere al tipo apropiado. –

+0

Si bien eso resolvería el problema como lo he presentado aquí, debería aclarar ... preferiría no tener 'SomeMethod()' devolver un 'objeto' general, porque' SomeMethod() 'también se llama en muchos otros lugares además de esta parte del código, y eso requeriría el lanzamiento en todos esos otros lugares. Gracias por la sugerencia, sin embargo. – jloubert

Respuesta

2

Normalmente agrego una interfaz que opera en tipos de 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(); 
    } 
} 

Por supuesto, depende de qué se está utilizando el resultado para.

A veces usted puede hacer que sea más fácil usar genéricos, pero también puede terminar en un lío. A veces es más fácil deprimirse en algún lugar. Por supuesto, intenta evitar esto cada vez que puede pagarlo.

+0

Esto es bastante agradable, ¡y probablemente sea algo en lo que debería haber pensado! Gracias por la respuesta. – jloubert

2

Aquí hay un ejemplo de cómo esto podría funcionar usando genéricos y haciendo que SuperFoo sea una clase abstracta.

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

podría reemplazar la interfaz ISuperFoo con una clase concreta, si es necesario, pero entonces tendría que emitir el valor de retorno qué tipo de derrota el propósito.

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

Gracias por la respuesta. Pero estoy un poco confundido por las clases FooReturn y Foo en la primera parte de tu respuesta. Si entiendo esto correctamente, parece que una instancia de SomeMethod() de Foo devolverá un objeto FooReturn en lugar de un objeto Foo. Entonces, todo el otro código que solía estar en Foo debería estar ahora en FooReturn, mientras que Foo solo necesita contener SomeMethod(). Si este es realmente el caso, ¿no rompería este código en algún otro lugar que tenga 'Foo fooObject = anotherFooObject.SomeMethod();'? – jloubert

+0

El tipo de devolución de SomeMethod siempre debe tratarse como ISuperFoo. Notarás que el varialbe T genérico abstracto de SuperFoo debe extender ISuperFoo.Esto es lo que le da el tipo de devolución consistente que se requiere. Agregué un ejemplo de barra a mi código que espero que aclare. – sgriffinusa

+0

También cambié el nombre de la clase secundaria de Foo a Baz, que con suerte aclarará aún más la diferencia entre la interfaz, el resumen y los tipos que se extienden. – sgriffinusa

Cuestiones relacionadas