2013-07-23 1 views
21

Ich versuche diese generische Methode zu erstellen, um die Dinge zu vereinfachen, aber ich denke, ich habe es versaut! Kannst du mir bei meinem Problem helfen?Generic mit mehreren Klassen

Dies kompiliert:

private string ConcatenateText<T1, T2>(MyEntity myEntity) 
    where T1 : Supplier, new() 
    where T1 : Employee, new() 
    where T2 : SupplierDepartment, new() 
    where T2 : EmployeeDepartment, new() 
{ 
    T1 p = new T1(); 
    T2 r = new T2(); 
    //Code here for myEntity treatment 
    return mystring; 
} 

Während dies nicht kompiliert:

protected void mybutton1_Click(object sender, EventArgs e) 
{ 
    string mystring = ConcatenaText<Supplier, SupplierDepartment>(myEntity); 
} 

//This does not compile 
protected void mybutton2_Click(object sender, EventArgs e) 
{ 
    string mystring = ConcatenaText<Employee, EmployeeDepartment>(myEntity); 
} 

Nachricht: Der Typ Lieferant nicht als Typ-Parameter T1 in dem generischen Typ oder Methode ConcatenateText verwendet werden können (MyEntity myEntity). Es gibt keine implizite Referenzkonvertierung vom Lieferanten zum Mitarbeiter

Kann dies getan werden? Was mache ich falsch? Kann es verbessert werden?

EDIT:

Und MyEntity ist nur eine andere Klasse, um es in dieser generischen Methode zu verarbeiten! Es ist nicht mit den Typen T verwandt. Es ist nur ein Argument. Aber es ist klar, dass ich das nicht tun kann, indem ich 2 Typen verwende. Ich dachte, dass ich das eine oder andere zuordnen könnte und die CLR unabhängig von meiner Initialisierung reagieren könnte wie ich wollte. Ich werde die Antwort akzeptieren, die ein bisschen mehr Informationen darüber gibt.

Antwort

24

Zu allererst Ihrem Code, der auf zwei Arten Abhängigkeiten zu setzen versucht generischer Parameter T1 kompiliert nicht

mit dem folgenden Fehler:

A constraint clause has already been specified for type parameter 'T1'. All of the constraints for a type parameter must be specified in a single where clause.

Wie MSDN-Artikel heißt es nur ein where Einschränkung für jeden generischen Parameter (http://msdn.microsoft.com/en-us/library/bb384067.aspx sehen) verwenden können.

"With multiple type parameters, use one where clause for each type parameter..."

Sie können auch nicht mehrere Klassennamen in eine Where-Einschränkung einfügen. Nur ein Klassenname und mehrere Schnittstellen.

where T1 : Supplier, IContractor, IComparable, new() 

Beachten Sie, dass diese Einschränkung schreibt vor, dass der tatsächliche Typ Sie als generischer Parameter liefern T1 ein Nachfolger des Lieferanten Klasse oder Lieferant Klasse selbst und es hat sein müssen beide IContractor UND IComparable Schnittstellen zu implementieren.

Sobald Ihre Methode ein MyEntity-Objekt akzeptiert und Sie nicht angeben, welche Beziehung es zu Mitarbeiter- und Lieferantenklassen hat, kann ich nicht erraten, wie diese MyEntity-Klasse Mitarbeiter- und Lieferantenklassen kennt und wie diese Beziehung Ihnen hilft.

Das einzige, was ich vorschlagen kann, ist entweder eine Schnittstelle oder eine Basisklasse zu erstellen und beide Klassen davon zu erben. Dies ist der einzige gute Grund, den ich sehe, um eine generische Methode zu erstellen. Es könnte wie folgt aussehen:

class Program 
{ 
    static void Main(string[] args) 
    { 
     Method1<Employee>(); 
     Method1<Supplier>(); 
    } 

    private static void Method1<T1>() 
     where T1 : IContractor, new() 
    { 

    } 
} 

public class Supplier : IContractor 
{ 
    string IContractor.Name 
    { 
     get{return "Supplier-Mufflier";} 
    } 
} 

public class Employee : IContractor 
{ 
    string IContractor.Name 
    { 
     get{return "Employee-Merloyee";} 
    } 
} 

public interface IContractor 
{ 
    string Name 
    { 
     get; 
    } 
} 

Wenn Ihre Klassen Lieferanten und Mitarbeiter nicht etwas Wichtiges gemeinsam haben, die für die Schaffung einer gemeinsamen Schnittstelle genug ist, könnten sie implementieren, dann sollten Sie nicht ein generisches Verfahren zur Verarbeitung von ihnen machen.

Erstellen Sie für jeden dieser Typen eine überladene Methode.

Stellen Sie sich vor, Sie haben zwei Klassen: Wife und Wine. Beide haben ein Attribut von Age und vom gleichen Typ auch. Aber denken Sie nicht einmal daran, eine gemeinsame Schnittstelle IAged für diese Klassen zu erstellen. Die Essenz der Klassen und die Bedeutung der Age ist so unterschiedlich, dass man sie niemals vereinheitlichen sollte. Trotzdem könnte eine allgemeine Logik Ihnen perfekt dienen. Zum Beispiel:

private double AgeQualify(Wife someWife) 
{ 
    return 1/(someWife.Age * someWife.Beachness); 
} 

private double AgeQualify(Wine someWine) 
{ 
    return someWine.Age/someWine.Sugar; 
} 
+0

oops, während ich meine Antwort schrieb zwei weitere erschien und fast die gleiche –

+0

Große Antwort sagen, aber wenn ich mich richtig erinnern Sie können nicht mehrere Klassen auf eine Klasse überlasten. So ist Interface und Generic in diesem Beispiel eine gute Alternative. –

+1

@ TarıkÖzgünGüner Überladung und Override nicht verwechseln. –

8

Sie müssen entweder separate Versionen machen:

private string ConcatenateText<T1, T2>(MyEntity myEntity) 
    where T1 : Supplier, new() 
    where T2 : SupplierDepartment, new() 
{ 
    T1 p = new T1(); 
    T2 r = new T2(); 
    return mystring; 
} 

private string ConcatenateText<T1, T2>(MyEntity myEntity) 
    where T1 : Employee, new() 
    where T2 : EmployeeDepartment, new() 
{ 
    T1 p = new T1(); 
    T2 r = new T2(); 
    return mystring; 
} 

oder müssen sie eine Basisklasse teilen lassen:

private string ConcatenateText<T1, T2>(MyEntity myEntity) 
    where T1 : EmployeeSuplierBase, new() 
    where T2 : EmployeeSupplierDeparmentBase, new() 
{ 
    T1 p = new T1(); 
    T2 r = new T2(); 
    return mystring; 
} 

ich die einzelnen Versionen bevorzugen würden, wirklich, denn mit ihnen sie kann es nicht mit Supplier und EmployeeDeparment (oder umgekehrt) anrufen

2

Sie sollten wirklich Generika in diesem Fall nicht verwenden. Es gibt nur zwei Optionen.

So:

string ConcatenateText(Supplier Entity) { ...code...} 
string ConcatenateText(Employee Entity) { ...code...} 

Was können Sie tun, ist sowohl mit einer Basisklasse vereinen alle gängigen Methoden konzentrieren.

Sag mal, wenn beiden Lieferanten und Mitarbeiter haben Name:

class BaseClass 
{ 
    public string Name {get; set;} 
} 

class Employee : BaseClass 
{ 
    //emplyee stuff except name and other things already in base 
} 

class Supplier : BaseClass 
{ 
    //supplier stuff except name and other things already in base 
} 

Und dann nimmt das Verfahren BaseClass:

private string ConcatenateText(BaseClass Entity) 
{ 
    //code 
}