2013-07-23 13 views
21

Sto provando a creare questo metodo generico per semplificare le cose ma penso di averlo incasinato! Puoi aiutare con il mio problema?Generico con più classi

Questo compila:

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

Mentre questo non compila:

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

messaggio: il tipo fornitore non può essere utilizzato come parametro di tipo T1 nel tipo generico o metodo ConcatenateText (MyEntity myEntity). Non esiste alcuna conversione implicita del riferimento dal Fornitore al Dipendente

Questo può essere fatto? Che cosa sto facendo di sbagliato? Può essere migliorato?

EDIT:

E MyEntity è solo un altro di classe al fine di elaborare all'interno di questo metodo generico! Non è legato ai tipi T. È solo un argomento. Ma è chiaro che non posso farlo, usando 2 tipi come questo. Ho pensato di poter assegnare uno o l'altro e il CLR indipendentemente dalla mia inizializzazione potrebbe reagire come volevo. Accetterò la risposta che condividerà un po 'più di informazioni a riguardo.

risposta

24

Prima di tutto, il codice che cerca di impostare due vincoli di tipo on il parametro generico T1 non viene compilato

where T1 : Supplier, new() 
where T1 : Employee, new() 

con il seguente errore:

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.

Come articolo di MSDN afferma è possibile utilizzare un solo where vincolo su ogni parametro generico (vedi http://msdn.microsoft.com/en-us/library/bb384067.aspx).

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

Inoltre, non è possibile inserire più nomi di classe in un vincolo "dove". Solo un nome di classe e diverse interfacce.

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

Tenete a mente che questo vincolo impone che il tipo effettivo che fornisci come il parametro generico T1 deve essere un successore della classe fornitore o fornitore classe stessa e deve implementare entrambe IContractor E IComparable interfacce.

Non appena il metodo accetta un oggetto MyEntity e non si specifica la relazione che ha con le classi Employee e Supplier, non posso immaginare come questa classe MyEntity conosca le classi Employee e Supplier e come questa relazione ti aiuti.

L'unica cosa che posso suggerire è creare un'interfaccia o una classe base ed ereditare entrambe le classi da esso. Questa è l'unica buona ragione che vedo per la creazione di un metodo generico. Potrebbe assomigliare a questo:

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

Se il fornitore classi e dei dipendenti non hanno qualcosa di importante in comune che è sufficiente per la creazione di un'interfaccia comune che potessero implementare allora non si dovrebbe fare un metodo generico per la loro elaborazione.

Creare un metodo sovraccarico per ciascuno di questi tipi.

Immagina di avere due classi: Wife e Wine. Entrambi hanno un attributo di Age e dello stesso tipo. Ma non pensare nemmeno di creare un'interfaccia comune IAged per quelle classi. L'essenza delle classi e il significato dello Age sono così diversi che non bisogna mai unificarli. Tuttavia una logica comune potrebbe perfettamente servirti. Per esempio:

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

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

oops, mentre stavo scrivendo la mia risposta un altro due apparvero e dicendo quasi la stessa –

+0

Grande risposta, ma se mi ricordavo correttamente non posso sovraccaricare più classi in una classe. Quindi Interface e generico sono una buona alternativa in questo esempio. –

+1

@ TarıkÖzgünGüner Non mescolare sovraccarico e override. –

8

vi sia bisogno di fare versioni separate:

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

o la necessità di farli condividere una classe base:

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

Preferirei le versioni separate, in realtà, perché con loro che non posso chiamarlo con Supplier e EmployeeDeparment (o viceversa)

2

In questo caso non dovresti proprio usare i generici. Ci sono solo due opzioni.

Quindi:

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

Che cosa si può fare è di unificare entrambe con una classe di base concentrando tutti i metodi comuni.

Dire, se entrambi fornitore e dipendente ha 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 
} 

E poi, il metodo prende BaseClass:

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