2016-06-15 15 views
1

Ich möchte ein Klassensystem erstellen, um die Daten aus einer bestimmten Datenquelle zu generalisieren und zu veröffentlichen. Ich habe zwei Methoden: GetData und PostData. Beide Methoden sollten eine Art von Eingabe haben, und GetData sollte auch Rückgabetyp haben. Ich habe versucht, generische Schnittstelle zu schreiben und es in „DatabaseSource“ Klasse zu implementieren:Generische Schnittstellenimplementierung: Generischer Rückgabetyp und generischer Eingabetyp

public class QueryParameter 
{ 
    public QueryParameter() 
    { 
     this.Direction = ParameterDirection.Input; 
    } 

    public string Name { get; set; } 
    public object Value { get; set; } 
    public ParameterDirection Direction { get; set; } 
} 

public class InputBase 
{ 
    public InputBase() 
    { 
     ResultMapping = new Dictionary<string, string>(); 
     Parameters = new List<QueryParameter>(); 
    } 
    public Dictionary<string, string> ResultMapping { get; set; } 
    public List<QueryParameter> Parameters { get; set; } 
} 
public class DatabaseInput: InputBase 
    { 
     public string Query { get; set; } 
     public DatabaseCommandType CommandType { get; set; } 
    } 

public interface IDataSource<I> where I: InputBase 
{ 
    IEnumerable<T> GetData<T>(I input); 
    void PostData(I Input); 
} 

Jetzt i-Schnittstelle wie dies umzusetzen versuche:

public class DatabaseDataSource: IDataSource<DatabaseInput> 
{ 
    public IEnumerable<T> GetData<T>(DatabaseInput Input) 
    { 
     //implementation 
    } 

    public void PostData(DatabaseInput Input) 
    { 
     //implementation 
    } 
} 

Aber ich habe ein Problem, wenn ich versuche, Daten zu instanziieren Quelle wie folgt aus:

IDataSource<InputBase> dataSource = new DatabaseDataSource(); 

ich Database nicht, weil dieser Code in einer Art Factory-Methode ist, und ich sollte in der Lage zu instanziiert andere IDataSource Implementierungen verwenden können.

Kurz gesagt, ich möchte Eingabe und Ausgabe als generische Typen haben und Constraint-Eingabe, um konkrete IDataSource-Implementierung zu entsprechen.

+0

Sie können dies über die Wo Operator http://StackOverflow.com/Questions/1096568/How-Cani-I-I-Interface-as-Ac-Sharp-Generic-Type-Constraint –

+0

Ich habe versucht, wo Operator verwenden , kann aber immer noch nicht wie im letzten Codeabschnitt in meiner Frage instanziieren – vpetrovic

+0

'DatabaseDataSource' ist eine' IDataSource 'nicht eine' IDataSource '. Sie können keine Kovarianz mit konkreten Typen durchführen. Siehe: https://msdn.microsoft.com/en-gb/library/dd799517(v=vs.110).aspx – TheInnerLight

Antwort

0

Sie können die Where-Einschränkung verwenden, indem Sie auf here klicken, um weitere Informationen zu Constraints zu erhalten.

where T : class //The type argument must be a reference type; this applies also to any class, interface, delegate, or array type. 

where T : <interface name> //The type argument must be or implement the specified interface. Multiple interface constraints can be specified. The constraining interface can also be generic. 
+0

Er versucht dies bereits, es ist nur die Semantik, die falsch sind. –

+0

Ich weiß, wo Constraint, aber ich kann es nicht bauen. Ich brauche ein konkretes Beispiel, ich hoffe, ich habe mein Problem in meiner Frage beschrieben, und wie Sie sehen können, habe ich versucht, 'wo' in der Interface-Definition zu verwenden. – vpetrovic

3

Wenn ich das richtig zu verstehen (und es ist zwar nicht 100% klar für mich), muss die Datenquelle sowohl den Eingang und die Ausgabe definieren. Wenn Sie dies tun:

public IEnumerable<T> GetData<T>(DatabaseInput Input) 

Dann wird die tatsächliche Umsetzung dieser Methode könnte erheblich variieren je nachdem, was T ist. Sie möchten kein Szenario dieser Methode, bei dem Sie den Typ T untersuchen und Ihren Code entsprechend verzweigen.

Vielleicht, was Sie wollen, ist so etwas wie dieses:

public interface IDataSource<TInput, TOutput> where TInput: InputBase 
{ 
    IEnumerable<TOutput> GetData(TOutput input); 
    void PostData(TInput Input); 
} 

Aber selbst dann haben Sie eine Schnittstelle erhalten haben, die zwei scheinbar nicht miteinander verbundenen Operationen ist zu definieren. (. Es scheint unwahrscheinlich, dass TInput kann sowohl als Abfrage verwendet werden, um Daten abzurufen und als Befehlsdaten zu schreiben/ändern)

Also vielleicht könnte man es brechen weiter unten:

public interface IDataSource<TInput, TOutput> where TInput: InputBase 
{ 
    IEnumerable<TOutput> GetData(TOutput input); 
} 

public interface IDataCommand<TInput> where TInput:InputBase 
{ 
    void PostData(TInput Input); 
} 

es zu instanziieren Sie könnten eine abstrakte Fabrik verwenden:

public interface IDataSourceFactory<TInput, TOutput> 
{ 
    IDataSource<TInput, TOutput> Create(); 
    void Release(IDataSource<TInput, TOutput> created); 
} 

der Grund dafür ist, weil es die Notwendigkeit für Ihre Klasse var dataSource = new [whatever]. anrufen vermeidet Wenn Sie das tun, dann besiegt er etwas den Zweck eine Schnittstelle zu implementieren. Unabhängig davon, welche Schnittstelle Sie implementieren, sobald Sie new explizit aufrufen und einen bestimmten Typ erstellen, ist Ihre Klasse an , Typ, nicht an die Schnittstelle gekoppelt.

Das verschiebt das ursprüngliche Problem. Was ist die Umsetzung der abstrakten Fabrik? Die gute Nachricht ist, dass die Klasse, die von der Fabrik abhängt, sich nicht um die Umsetzung der Fabrik kümmert. Aber du brauchst noch eins.Eine Möglichkeit, dies zu tun, ist die Verwendung eines DI-Containers. Windsor ist hilfreich, da es ein Muster für die Erstellung abstrakter Fabriken bietet. This blog post beschreibt, wie man das genauer macht.

+0

Scott, ich werde deine Antwort genauer lesen. Ich möchte nur verdeutlichen, was InputBase ist: Es ist Basisklasse für die Bereitstellung von Parametern zum Filtern von Daten (für GetData) oder Parameter für einige Update-Abfrage (für PostData). Und wie bei TResult - ich möchte mit Reflection Daten in TResult-Objekte laden. – vpetrovic

+0

Das habe ich mir gedacht. Wenn Sie "public interface IDataSource wo I: InputBase" deklarieren und diese Methode sowohl einen "get" als auch einen "post" hat, nehmen beide 'I' als Parameter, dann bedeutet das genau das gleiche' I' (Implementierung von 'InputBase') muss sowohl als Abfrageparameter als auch als Update/Insert/Delete-Parameter verwendbar sein. In der Praxis wird ein Abfrageparameter jedoch anders sein. Wenn eine Entität beispielsweise ein Datum hat, enthält die Abfrage möglicherweise Start- und Enddatumsparameter, während ein Befehl zum Einfügen oder Aktualisieren ein tatsächliches Datum und keinen Abfragebereich enthält. –