2012-03-30 7 views
9

Sé que no estoy haciendo esto bien, pero también sé que hay una manera de hacerlo. Intento ser lo más genérico y abstracto posible; de ​​lo contrario, mi código se volverá realmente complicado. Así que estoy usando el patrón de estrategia aquí como el infierno, que es el método GetAggregateClient().¿La mejor manera de hacer esta clase abstracta genérica en C#?

Quiero tener una clase abstracta llamada AbstractAggregate, para que use genéricos. El tipo que se utilizará es una serie de clases de datos que son BlogItem, ResourceItem y AskItem. Todas estas clases de datos heredan de ListItem.

Esa es la información de fondo. El problema aquí es que quiero que GetAbstractAggregate() devuelva una instancia de una de las clases de cliente que implementa AbstractAggregate con el tipo de elemento especificado en función de la enum transferida. Sin embargo, no puedo devolver un "ResumenAgregado". El compilador no me deja y eso tiene sentido ya que la clase AbstractAggregateFactory no es genérica.

¿Alguien tiene la mejor manera de hacer esto?

Muchas gracias.

public static class AggregateHelper 
{ 
    public enum AggregateTypes { TankTruckBlog, AskTankTruck, Resources } 
} 

public static class AbstractAggregateFactory 
{ 
    public static AbstractAggregate<T> GetAggregateClient(AggregateHelper.AggregateTypes type) 
    { 
     switch (type) 
     { 
      case AggregateHelper.AggregateTypes.AskTankTruck: 
       return new AskTankTruckAggregate<AskItem>(); 
      case AggregateHelper.AggregateTypes.TankTruckBlog: 
       return new TankTruckBlogAggregate<BlogItem>(); 
      case AggregateHelper.AggregateTypes.Resources: 
       return new ResourcesAggregate<ResourceItem>(); 
      default: 
       throw new AggregateDoesNotExistException(); 
     } 
    } 
} 

public abstract class AbstractAggregate<T> 
{ 
    public abstract List<T> GetAggregate(Guid[] resourcetypes); 

    public abstract T GetSingle(string friendlyname); 


} 

public class AskTankTruckAggregate<T> : AbstractAggregate<T> 
{ 
    //not implemented yet 
} 

public class TankTruckBlogAggregate<T> : AbstractAggregate<T> 
{ 
    //not implemented yet 
} 

public class ResourcesAggregate<T> : AbstractAggregate<T> 
{ 
    //not implemented yet 
} 

Respuesta

3

el problema, el compilador se queja de
... es que tienes un método que es ' abrir '(T) - y está volviendo genérico cerrado (con <AskItem> etc.), tipo concreto en realidad.
es decir, debe devolver un ... <T> - ... y puede hacerlo con el método; no importa si la fábrica no es genérica, el método puede ser inmóvil.
En cuanto a cuál es la mejor manera de hacerlo,
que es una pregunta 'diseño' más - y la historia un poco más,
No estoy del todo seguro de lo que estamos tratando de lograr (tal vez algo de historia de fondo, cómo tanto los tipos que podrían tener, etc.),

en primer lugar, no se debe (en términos generales, como una buena práctica o algún 'se siente bien' factor)
heredan sus artículos de ListItem - utilizar alguna otra clase de base de los suyos - y si necesita una colección use una genérica como List<T> - o cree su propia implementación de IList, etc.

En segundo lugar, la cosa es que no necesita todo lo genérico. Su agregador base es genérico, pero las clases personalizadas no son, por lo general, p. como este ...

abstract class ItemBase { } 
class AskItem : ItemBase { } 
class BlogItem : ItemBase { } 
class ProvderA : ProviderBase<AskItem> 
{ 
    public override AskItem Get() 
    { 
     throw new NotImplementedException(); 
    } 
} 
class ProvderB : ProviderBase<BlogItem> 
{ 
    public override BlogItem Get() 
    { 
     throw new NotImplementedException(); 
    } 
} 
abstract class ProviderBase<T> where T : ItemBase 
{ 
    public abstract T Get(); 
} 
class Program 
{ 
    static void Main(string[] args) 
    { 
     ProviderBase<AskItem> provider = GetProvider<AskItem>(); 
     var item = provider.Get(); 
    } 
    static ProviderBase<T> GetProvider<T>() where T : ItemBase 
    { 
     if (typeof(T) == typeof(AskItem)) 
      return (ProviderBase<T>)(object)new ProvderA(); 
     if (typeof(T) == typeof(BlogItem)) 
      return (ProviderBase<T>)(object)new ProvderB(); 
     return null; 
    } 
} 

... esa es una implementación.
Básicamente, no todo lo 'genérico' es siempre la mejor manera. Debe tener suficientes razones o 'tipos' desconocidos para ser usados ​​posiblemente. Al igual que con los genéricos, también paga un cierto precio. Cruzar genéricos al mundo no genérico suele ser complicado e implica reflexión si los tipos no se pueden inferir por el uso, etc.
El error de IMO hace que cada proveedor sea genérico, ya que solo acepta un tipo (cada uno concreto), mientras la base es genérica Así que como el de arriba. Generalmente genérico también está restringido por interfaz donde/donde se puede.
Pero luego tienes un problema ya que volver al contexto genérico de una clase no genérica no es correcta (también ten en cuenta que hay advertencias con tipos de valores ya que tienes que tratar eso de manera diferente a veces, a menudo), y viceversa versa también.
Por lo tanto, primero necesita algo como lanzar (objeto).
Prefiero usar el enfoque del COI aquí, por ejemplo. mira el autofac (no estoy asociado, pero me gusta cómo funciona, buen marco de trabajo). En ese caso, usted haría algo como ...

 container.Register<ProviderBase<AskItem>>(c=> new ProvderA()); 
     container.Register<ProviderBase<BlogItem>>(c => new ProvderB()); 

     // and query later... 

     ProviderBase<AskItem> provider = container.Resolve<ProviderBase<AskItem>>(); 

esperanza de que esto ayude a algunos ...

+0

gracias por la explicación detallada. originalmente elegí una respuesta diferente, pero me convenciste. ¡gracias por la ayuda! – apexdodge

+0

np :) - hay muchas formas/respuestas, simplemente mantenlo lógico (cada bit usualmente tiene su propósito o no garantiza un lugar) y estarás bien. Y eche un vistazo a IOC, autofac, le gustará eso (más que genéricos :) – NSGaga

1

No estoy seguro de entender lo que está tratando de lograr, pero tal vez es algo como esto

public static class AbstractAggregateFactory 
{ 
    public static AbstractAggregate<T> GetAggregateClient<T>() 
    { 
     if(T is AskItem) return new AskTankTruckAggregate(); 
     if(T is BlogItem) return new TankTruckBlogAggregate(); 
     if(T is ResourceItem) return new ResourcesAggregate(); 
    } 
} 

public abstract class AbstractAggregate<T> 
{ 
    public abstract List<T> GetAggregate(Guid[] resourcetypes); 

    public abstract T GetSingle(string friendlyname); 
} 

public class AskTankTruckAggregate : AbstractAggregate<AskItem> 
{ 
    //not implemented yet 
} 

public class TankTruckBlogAggregate : AbstractAggregate<BlogItem> 
{ 
    //not implemented yet 
} 

public class ResourcesAggregate : AbstractAggregate<ResourceItem> 
{ 
    //not implemented yet 
} 
+0

No creo que se puede hacer 'T es BlogItem' - se necesita un valor de T - o 'typeof (T) .equals (typeof (BlogItem))' o algo así - al igual que una anotación al margen – NSGaga

+0

@NSGaga: Sí, probablemente tengas razón. – Gebb

1

Estoy tratando de ser lo más genérica y abstracta de lo posible, de lo contrario mi el código se volverá realmente desordenado.

esto es un error. ser genérico/abstracto en realidad puede complicar un problema simple. La clave para limpiar el código es la encapsulación. muy diferente a la herencia o genéricos.

En este caso, creo que la composición sería una mejor opción, en lugar de la herencia. con un conjunto de adaptadores podría tener un objeto común al que se pudiera adaptar cada entidad. por ejemplo:

interface ICommon { ... } 

class AskAdaptor: ICommon 
{ 
    private readonly Ask ask; 
    publick AskAdaptor(Ask ask) 
    { 
     this.ask = ask; 
    } 
} 

class AskAdaptor: ICommon 
{ 
    private readonly Blog blog; 
    publick AskAdaptor(Blog blog) 
    { 
     this.blog = blog; 
    } 
} 

class AskAdaptor: ICommon 
{ 
    private readonly Resource resource; 
    publick AskAdaptor(Resource resource) 
    { 
     this.resource = resource; 
    } 
} 

class CommonAggregate 
{ 
    public void Add(ICommon common) 
    { 
     .... 
    } 
} 
1

¿Qué tal esto:

public static class AggregateHelper 
{ 
    public enum AggregateTypes { TankTruckBlog, AskTankTruck, Resources } 
} 

public class AskItem { } 
public class BlogItem { } 
public class ResourceItem { } 

public static class AbstractAggregateFactory 
{ 
    public static AbstractAggregate<T> GetAggregateClient<T> 
     (AggregateHelper.AggregateTypes type) 
    { 
     switch (type) 
     { 
      case AggregateHelper.AggregateTypes.AskTankTruck: 
       return new AskTankTruckAggregate<T>(); 
      case AggregateHelper.AggregateTypes.TankTruckBlog: 
       return new TankTruckBlogAggregate<T>(); 
      case AggregateHelper.AggregateTypes.Resources: 
       return new ResourcesAggregate<T>(); 
      default: 
       throw new ArgumentException(); 
     } 
    } 
} 

public abstract class AbstractAggregate<T> 
{ 
    public abstract List<T> GetAggregate(Guid[] resourcetypes); 
    public abstract T GetSingle(string friendlyname); 
} 

public class AskTankTruckAggregate<T> : AbstractAggregate<T> 
{ 
    public override List<T> GetAggregate(Guid[] resourcetypes) 
    { 
     throw new NotImplementedException(); 
    } 

    public override T GetSingle(string friendlyname) 
    { 
     Console.WriteLine(friendlyname); 
     Type whats_t = typeof(T); 
     return default(T); 
    } 
} 

public class TankTruckBlogAggregate<T> : AbstractAggregate<T> 
{ 
    //not implemented yet 
} 

public class ResourcesAggregate<T> : AbstractAggregate<T> 
{ 
    //not implemented yet 
} 

Ejemplo:

AbstractAggregate<BlogItem> foo3 = 
    AbstractAggregateFactory.GetAggregateClient<BlogItem>(AggregateHelper.AggregateTypes.AskTankTruck); 
foo3.GetSingle("test"); 
0

Una cosa que es posiblemente claro es que su diseño es bastante imperfecto. Un tipo de cambio no es lo mejor que se puede hacer en un método genérico que anula su propósito. Pero lo que no está claro es cuál es el propósito de tus clases.

Algunas especulaciones:

1) ver a sus clases par AskItem y AskTankTruckAggregate<T> etc no creo que este último tiene que ser una clase genérica, es una clase muy específica, estrechamente unida a AskItem. Me rediseñarlo como

public static class AbstractAggregateFactory 
{ 
    public static AbstractAggregate<T> GetAggregateClient<T>() where T : ListItem 
    { 
     //use reflection to find the type that inherits AbstractAggregate<T> 

     //instantiate the type 

     //cast to AbstractAggregate<T> and return 
    } 
} 

public class AskTankTruckAggregate : AbstractAggregate<AskItem> 
{ 
    //not implemented yet 
} 

public class TankTruckBlogAggregate : AbstractAggregate<BlogItem> 
{ 
    //not implemented yet 
} 

public class ResourcesAggregate : AbstractAggregate<ResourceItem> 
{ 
    //not implemented yet 
} 

llamar así:

AbstractAggregateFactory.GetAggregateClient<AskItem>(); //etc 

2) Otra forma: delegar la creación de empleo agregado a sus ListItems.

public abstract class ListItem //or interface 
{ 
    protected abstract object Create(); 
} 
public class AskItem : ListItem { //implement to return AskTankTruckAggregate 
} 
public class BlogItem : ListItem { //implement to return TankTruckBlogAggregate 
} 
public class ResourceItem : ListItem { //implement to return ResourcesAggregate 
} 

public static class AbstractAggregateFactory 
{ 
    public static AbstractAggregate<T> GetAggregateClient<T>() where T : ListItem, new() 
    { 
     return (AbstractAggregate<T>)new T().Create(); 
    } 
} 

public class AskTankTruckAggregate : AbstractAggregate<AskItem> 
{ 
    //not implemented yet 
} 

public class TankTruckBlogAggregate : AbstractAggregate<BlogItem> 
{ 
    //not implemented yet 
} 

public class ResourcesAggregate : AbstractAggregate<ResourceItem> 
{ 
    //not implemented yet 
} 

llamar así:

AbstractAggregateFactory.GetAggregateClient<AskItem>(); //etc 

3) o de la misma, pero que sea un poco más inflexible de tipos, con el uso de los genéricos:

public abstract class ListItem<T> where T : ListItem<T> //or interface 
{ 
    protected abstract AbstractAggregate<T> Create(); 
} 
public class AskItem : ListItem<AskItem> { //implement to return AskTankTruckAggregate 
} 
public class BlogItem : ListItem<BlogItem> { //implement to return TankTruckBlogAggregate 
} 
public class ResourceItem : ListItem<ResourceItem> { //implement to return ResourcesAggregate 
} 

public static class AbstractAggregateFactory 
{ 
    public static AbstractAggregate<T> GetAggregateClient<T>() where T : ListItem, new() 
    { 
     return new T().Create(); 
    } 
} 

public class AskTankTruckAggregate : AbstractAggregate<AskItem> 
{ 
    //not implemented yet 
} 

public class TankTruckBlogAggregate : AbstractAggregate<BlogItem> 
{ 
    //not implemented yet 
} 

public class ResourcesAggregate : AbstractAggregate<ResourceItem> 
{ 
    //not implemented yet 
} 

Call es como:

AbstractAggregateFactory.GetAggregateClient<AskItem>(); //etc 

4) Por último, ¿puede hacer que el tipo de devolución sea menos genérico? Implica la caja del interruptor, no me gusta.

public enum AggregateTypes { TankTruckBlog, AskTankTruck, Resources } 

public static class AbstractAggregateFactory 
{ 
    public static AbstractAggregate GetAggregateClient(AggregateTypes type) 
    { 
     switch (type) 
     { 
      case AggregateTypes.AskTankTruck: 
       return new AskTankTruckAggregate<AskItem>(); 
      case AggregateTypes.TankTruckBlog: 
       return new TankTruckBlogAggregate<BlogItem>(); 
      case AggregateTypes.Resources: 
       return new ResourcesAggregate<ResourceItem>(); 
      default: 
       throw new AggregateDoesNotExistException(); 
     } 
    } 
} 

public abstract class AbstractAggregate 
{ 

} 

public abstract class AbstractAggregate<T> : AbstractAggregate 
{ 

} 

//or change the definition to AskTankTruckAggregate : AbstractAggregate<AskItem> 
public class AskTankTruckAggregate<T> : AbstractAggregate<T> 
{ 
    //not implemented yet 
} 

//or change the definition to TankTruckBlogAggregate : AbstractAggregate<BlogItem> 
public class TankTruckBlogAggregate<T> : AbstractAggregate<T> 
{ 
    //not implemented yet 
} 

//or change the definition to ResourcesAggregate : AbstractAggregate<ResourceItem> 
public class ResourcesAggregate<T> : AbstractAggregate<T> 
{ 
    //not implemented yet 
} 

llamar así:

AbstractAggregateFactory.GetAggregateClient(AggregateTypes.AskTankTruck); //etc 

Imo, este enfoque es peor que el enfoque de la reflexión. Es fácil olvidar algunas verificaciones enum en el futuro.


De todos, el tercero parece el mejor para mí, pero de nuevo sin conocer su objetivo de diseño, es muy difícil de predecir. Algunas sugerencias:

  1. Su nombre de fábrica suena mejor como AggregateFactory. "Resumen" en él lo hace más acerca de la implementación.

  2. En caso de que necesite un tipo de enumeración para denotar, no lo haga anidado. Los tipos públicos anidados son más difíciles de llamar. Elimine la clase estática de envoltura (como en mi 5º enfoque).

  3. Cambie el nombre de su clase base como Aggregate<T> o . Una vez más, "Resumen" lo hace más acerca de la implementación, bastante innecesario.

Cuestiones relacionadas