2012-03-30 8 views
9

Ich weiß, dass ich das nicht richtig mache, aber ich weiß auch, dass es einen Weg gibt, dies zu tun. Ich versuche, so generisch und abstrakt wie möglich zu sein, sonst wird mein Code wirklich unordentlich. Also verwende ich hier ein Strategie-Muster als Hölle, das ist die GetAggregateClient() Methode.Der beste Weg, um diese generische abstrakte Klasse in C# zu machen?

Ich möchte eine abstrakte Klasse namens AbstractAggregate, so dass es Generika verwendet. Der Typ, der verwendet wird, ist eine Reihe von Datenklassen, die BlogItem, ResourceItem und AskItem sind. Diese Datenklassen erben alle von ListItem.

Das ist also die Hintergrundinfo. Das Problem hier ist, dass ich möchte, dass GetAbstractAggregate() eine Instanz einer der Client-Klassen zurückgibt, die AbstractAggregate mit dem Typ des angegebenen Elements in Abhängigkeit von der übergebenen Enum implementiert. Ich kann jedoch kein "AbstractAggregate" zurückgeben. Compiler lässt mich nicht und das macht Sinn, da die AbstractAggregateFactory-Klasse kein generisches ist.

Hat jemand den besten Weg, dies zu tun?

Vielen Dank.

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 
} 

Antwort

3

das Problem, Compiler beschwert sich über
... ist, dass Sie eine Methode, die ist ' öffnen '(T) - und Sie sind geschlossen generische (mit <AskItem> etc.), konkrete Art wirklich.
d. H. Sie müssen eine ... <T> - ... und Sie können das mit der Methode tun - egal, ob die Fabrik nicht generisch ist, kann Methode immer noch sein.
Als für das, was ist der beste Weg, es zu tun,
, die eine ‚Design‘ Frage mehr ist - und ein bisschen mehr Geschichte,
ich bin nicht ganz sicher, was Sie (vielleicht einige Hintergrundinformationen Geschichte erreichen wollen, wie viel Typen, die Sie könnten usw. haben),

Zunächst sollten Sie nicht (allgemein gesprochen, als best Practice oder einige ‚fühlt sich gut an‘ -Faktor)
Ihre Artikel von ListItem erben - eine andere Basisklasse von Ihnen verwenden - Und wenn Sie eine Sammlung benötigen, verwenden Sie eine generische wie List<T> - oder erstellen Sie Ihre eigene IList-Implementierung usw.

Zweitens ist die Sache, dass Sie nicht alles generisch brauchen. Ihr Basisaggregator ist generisch, aber benutzerdefinierte Klassen sind normalerweise nicht z. so ...

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

... das ist eine Implementierung.
Grundsätzlich ist nicht alles "generisch" immer der beste Weg. Sie müssen genügend Gründe oder "Typen" haben, die möglicherweise nicht verwendet werden können. Wie bei Generika bezahlen Sie auch einen bestimmten Preis. Das Überqueren von Generika mit Nicht-Generika ist oft schwierig und beinhaltet Überlegungen, ob Ihre Typen nicht durch die Verwendung usw. abgeleitet werden können.
IMO-Fehler macht jeden Anbieter zu einem generischen - da er nur einen Typ (jeden konkreten) akzeptiert Basis ist generisch. So wie oben. In der Regel wird generisch auch per Schnittstelle eingeschränkt, wo/wo Sie können.
Aber dann haben Sie ein Problem, wie zurück zu generischen Kontext aus einer effektiv eine nicht-generische Klasse ist nicht gerade (auch im Hinterkopf haben Vorbehalte mit Werttypen, wie Sie manchmal anders oft behandeln müssen), und umgekehrt auch umgekehrt.
Daher benötigen Sie zuerst etwas wie Cast (Objekt).
Ich würde lieber eine Art von IOC Ansatz hier - z. schau dir die autofac an (ich bin nicht verbunden, aber ich mag, wie es funktioniert, schönes Framework). In diesem Fall würden Sie so etwas wie ...

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

     // and query later... 

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

hoffe, das hilft etwas ...

+0

danke für die detaillierte Erklärung. Ich habe ursprünglich eine andere Antwort gewählt, aber Sie haben mich überzeugt. Danke für die Hilfe! – apexdodge

+0

np :) - es gibt viele Möglichkeiten/Antworten, bleibe einfach dabei, es logisch zu halten (jedes Bit hat normalerweise seinen Zweck oder gibt keinen Platz) und dir wird es gut gehen. Und schau dir IOC, Autofac an, das wird dir gefallen (mehr als Generika :) – NSGaga

1

Ich bin nicht sicher, ob ich verstehe, was Sie versuchen, es zu erreichen, aber vielleicht so etwas wie diese

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

Ich glaube nicht, Sie tun können 'T ist BlogItem' - Sie einen Wert von T müssen - oder 'typeof (T) .Equals (typeof (BlogItem))' oder etwas - nur als Nebenbei bemerkt – NSGaga

+0

@ NSGaga: Ja, wahrscheinlich hast du recht. – Gebb

1

Ich versuche als generisches und abstrakt wie möglich zu sein, sonst meine Der Code wird wirklich unordentlich.

Dies ist ein Missverständnis. generisch/abstrakt zu sein, kann ein ansonsten einfaches Problem komplizieren. Der Schlüssel zum sauberen Code ist die Kapselung. viel anders als diese Vererbung oder Generika.

In diesem Fall denke ich, Zusammensetzung wäre eine bessere Wahl, als Vererbung. Mit einer Reihe von Adaptern könnten Sie ein gemeinsames Objekt haben, an das jede Entität angepasst werden könnte. zum Beispiel:

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

Wie wäre es damit:

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 
} 

Beispiel:

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

Eine Sache tun, die möglicherweise klar ist, ist, dass Ihr Design etwas fehlerhaft ist. Ein Switch-Typ ist nicht das Beste, was man in einer generischen Methode tun kann, die ihren Zweck vereitelt. Aber was nicht klar ist, ist der Zweck Ihrer Klassen.

Einige Spekulationen:

1) Ihr Paar Klassen sehen AskItem und AskTankTruckAggregate<T> usw. Ich denke nicht, letztere eine generische Klasse zu sein hat, ist es eine sehr spezielle Klasse, eng gekoppelt an AskItem. Ich würde es neu zu gestalten wie

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 
} 

Nennen Sie es mögen:

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

2) Eine andere Möglichkeit: delegieren die aggregierte Schaffung Auftrag an Ihren 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 
} 

Nennen Sie es mögen:

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

3) oder gleich sein, aber machen es ein bisschen stark typisierte, mit der Verwendung von Generika:

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 
} 

Anruf es mag:

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

4) Schließlich kann der Rückgabetyp weniger generisch sein? Bezieht Schalterkasten mit ein, ich mag es nicht.

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 
} 

Nennen Sie es mögen:

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

Imo ist dieser Ansatz schlechter als die Reflexion Ansatz. Es ist leicht zu vergessen, einige Enum Überprüfung in der Zukunft.

Von allen, 3. sieht das Beste für meine Augen, aber wieder ohne zu wissen, Ihr Design Ziel ist es sehr schwer vorherzusagen. Einige Vorschläge:

  1. Ihr Fabrikname klingt besser wie AggregateFactory. "Abstrakt" macht es mehr über die Implementierung.

  2. Falls Sie eine Aufzählung benötigen, um den Typ zu bezeichnen, machen Sie ihn nicht verschachtelt. Verschachtelte öffentliche Typen sind schwieriger zu rufen. Entnehmen Sie die statische Klasse (wie in meinem 5. Ansatz).

  3. Benennen Sie Ihre Basisklasse als Aggregate<T> oder um. Nochmals "abstrakt" macht es mehr um Umsetzung, ganz unnötig.

Verwandte Themen