2016-04-27 11 views
1

Ich habe eine Basisklasse, die generisch ist. Ich habe eine konkrete Klasse, die die Basisklasse implementiert.C# Factory für die konkrete Implementierung der generischen Basisklasse

Wie würde ich eine Factory-Klasse/Methode für die Lieferung verschiedener Arten von Betonklassen erstellen?

Hier ein Beispiel:

public class ReceiverBase<T> 
    where T : IInterpreter 
{ ... } 

public class SpecialReceiver : ReceiverBase<OwnInterpreter> { ... } 

public class ReceiverFactory<T> 
    where T : ReceiverBase<IInterpreter>, new() 

    public T Create(string type) { 
     switch(type) { 
      default: 
       return new SpecialReceiver(); 
     } 
    } 
} 

Das Problem ist, dass ReceiverBase nicht möglich zu sein scheint, da die Compiler nur Klassen als Constraints wollen, nicht-Schnittstellen. Und das zweite Problem ist, dass ich SpecialReceiver nicht in T konvertieren kann.

Also gibt es eine Möglichkeit, das funktioniert zu bekommen?

=== EDIT: Hinzugefügt Beispiel gemäß erste Antwort ===

public interface IInterpreter 
{ 
} 

public class OwnInterpreter : IInterpreter 
{ 
    public void Dispose() 
    { 
     throw new NotImplementedException(); 
    } 

    public void DoSomething() { } 
} 



public abstract class ReceiverBase<T> 
    where T : IInterpreter 
{ 
    public T MyReceiver { get; set; } 

    internal abstract void Start(); 
} 

public class SpecialReceiver<T> : ReceiverBase<T> 
    where T : IInterpreter, new() 
{ 
    public void CheckSomething() 
    { 
     MyReceiver.DoSomething(); 
    } 

    internal override void Start() 
    { 
     MyReceiver = new T(); 
    } 
} 

public class ReceiverFactory<T> 
    where T : IInterpreter, new() 
{ 
    public static ReceiverBase<T> Create(string type) 
    { 
     switch (type) 
     { 
      default: 
       return new SpecialReceiver<T>(); 
     } 
    } 
} 

Das Problem ist: MyReceiver.DoSomething(); wird nicht funktionieren. Außerdem hätte ich das Werk so nennen: ReceiverFactory<OwnInterpreter>.Create(""); ich möchte es haben auf diese Weise: ReceiverFactory.Create("SpecialReceiver");

+0

Ihr erster Kommentar ist überhaupt nicht wahr. Sie können sicherlich Schnittstellen als Einschränkungen haben. Was Sie jedoch erleben können, ist, dass Sie, weil Sie in der Lage sein wollen, neue Instanzen des Typs, der eingeschränkt ist, in der Lage sind, die Einschränkung "new()' einzuschließen, wie Sie haben. –

+0

Compiler sagt: Der Typ IInterpreter muss einen öffentlichen parameterlosen Konstruktor haben, um ihn als Parameter T zu verwenden. Der Compiler bezieht sich auf diese Zeile: wo T: ReceiverBase , new() – DARKHalf

+0

OK, ich bin korrigiert. Sie * können * Interfaces als Constraints haben, aber in diesem Fall können Sie nicht, weil Sie 'new()' verwenden wollen, und Sie müssen garantieren, dass der Konstruktor existiert. Schnittstellen garantieren keine Konstruktoren. –

Antwort

0

können Sie generische Methode in der Fabrik verwendet werden:

class Program 
{ 
    static void Main(string[] args) 
    { 
     var own = ReceiverFactory.Create<OwnInterpreter>(); 
     var other = ReceiverFactory.Create<OtherInterpreter>(); 
     own.Start(); 
     other.Start(); 
     Console.ReadLine(); 
    } 
} 
interface IInterpreter 
{ 
    void DoSomething(); 
} 

class OwnInterpreter : IInterpreter 
{ 
    public void DoSomething() { Console.WriteLine("Own"); } 
} 

class OtherInterpreter : IInterpreter 
{ 
    public void DoSomething() { Console.WriteLine("Other"); } 
} 

abstract class ReceiverBase<T> where T: IInterpreter, new() 
{ 
    public T Interpreter { get; set; } 
    public ReceiverBase() 
    { 
     Interpreter = new T(); 
    } 
    public void Start() 
    { 
     Interpreter.DoSomething(); 
    } 
} 

class SpecialReceiver : ReceiverBase<OwnInterpreter> { } 
class OtherReceiver : ReceiverBase<OtherInterpreter> { } 

static class ReceiverFactory 
{ 
    private static Dictionary<string, object> factories = new Dictionary<string, object>(); 
    static ReceiverFactory() 
    { 
     RegisterFactory(() => new SpecialReceiver()); 
     RegisterFactory(() => new OtherReceiver()); 
    } 
    public static void RegisterFactory<T>(Func<ReceiverBase<T>> factory) where T : IInterpreter, new() 
    { 
     factories.Add(typeof(T).FullName, factory); 
    } 
    public static ReceiverBase<T> Create<T>() where T : IInterpreter, new() 
    { 
     var type = typeof(T); 
     return ((Func<ReceiverBase<T>>)factories[type.FullName]).Invoke(); 
    } 
} 

In der Tat, Sie hier nicht brauchen "new()" Zwang, da Sie Fabriken verwenden.

+0

Danke für die nette Probe, aber es gibt ein allgemeines Problem hier.Ich möchte nicht die Interpreter unterscheiden, sondern die Receiver-Klassen. In meinem Entwurfsproblem habe ich verschiedene Arten von Informationsquellen, die gleich sind. In meinem Fall ist dies ein Ort. (GPS, Zelle usw.) Jede Quelle bietet Breiten- und Längengrad. Ich möchte jetzt eine Fabrik haben, die die Koordinaten liefert. Ich möchte nur die Fabrik anrufen mit Create ("GPS") oder Create ("Cell") oder irgendetwas anderem, das diese Art von Informationen liefern wird. – DARKHalf

0

Ich schlage vor, Sie Ihren Code zu ändern:

public class ReceiverBase<T> where T : IInterpreter 
    { 
    } 

    public interface IInterpreter 
    { 
    } 

    public class SpecialReceiver<T> : ReceiverBase<T> 
     where T : IInterpreter 
    { 
    } 

    public class OwnInterpreter : IInterpreter 
    { 
    } 

    public class ReceiverFactory<T> where T : IInterpreter, new() 
    { 
     public ReceiverBase<T> Create(string type) 
     { 
      switch (type) 
      { 
       default: 
        return new SpecialReceiver<T>(); 
      } 
     } 
    } 

Der Grund, warum können Sie nicht nur Rückgabe T in Ihrem Fall ist, dass es keine implizite Konvertierung zwischen SpecialReceiver und ReceiverBase<IInterpreter> gibt.

+0

Das würde zu zwei anderen Problemen führen: Ich konnte Code in SpecialReceiver, der OwnInterreter-Instanzen verwendet, nicht implementieren. Ich müsste die Fabrik mit einem Beton IIinterpreter aufrufen. in diesem Fall wäre es OwnInterpreter. Die Fabrik sollte nur einen String-Parameter benötigen, der von einer Konfiguration oder anderswo gelesen werden kann. – DARKHalf

+0

@DARKHalf welche sind? :) – Kamo

+0

Bearbeitete meine ursprüngliche Frage – DARKHalf

0

Ich konnte eine Lösung finden, die meinen Bedürfnissen entspricht. Ich habe eine andere Schnittstelle IReciver hinzugefügt, die die Eigenschaften und Mitglieder definiert, die ich wirklich brauche. Die Factory-Methode gibt IReceiver zurück, sodass ich alle Bindungsprobleme mit Generics weglassen kann. Manchmal ist es einfach so. :)

public interface IInterpreter { } 

public interface IReceiver 
{ 
    bool Enabled { get; set; } 
} 

public class OwnInterpreter : IInterpreter 
{ 
    public void DoSomething() { } 
} 

public abstract class ReceiverBase<T> : IReceiver 
    where T : IInterpreter, new() 
{ 
    public T MyReceiver { get; set; } 

    internal abstract void Start(); 

    private bool _isEnabled; 
    public bool Enabled { get { return _isEnabled; } set { _isEnabled = value; OnEnable(value); } } 

    internal abstract void OnEnable(bool isEnabled); 

    protected ReceiverBase() 
    { 
     MyReceiver = new T(); 
    } 
} 

public class SpecialReceiver : ReceiverBase<OwnInterpreter> 
{ 
    public void CheckSomething() 
    { 
     MyReceiver.DoSomething(); 
    } 

    internal override void Start() 
    { 
     // just for testing puropses 
     MyReceiver = new OwnInterpreter(); 
    } 

    internal override void OnEnable(bool isEnabled) 
    { 
     MyReceiver = isEnabled ? new OwnInterpreter() : null; 
    } 
} 

public class ReceiverFactory 
{ 
    public static IReceiver Create(string type) 
    { 
     switch (type) 
     { 
      default: 
       return new SpecialReceiver(); 
     } 
    } 
} 

public class Program 
{ 
    [STAThread] 
    public static void Main() 
    { 
     ReceiverFactory.Create(""); 
    } 
}