web-dev-qa-db-fra.com

Générique avec plusieurs classes

J'essaie de créer cette méthode générique pour simplifier les choses mais je pense que je l'ai foiré! Pouvez-vous m'aider avec mon problème?

Cela compile:

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

Bien que cela ne compile pas:

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

Message: Le type Supplier ne peut pas être utilisé comme paramètre de type T1 dans le type ou la méthode générique ConcatenateText (MyEntity myEntity). Il n'y a pas de conversion de référence implicite de Supplier en Employee

Cela peut-il être fait? Qu'est-ce que je fais mal? Peut-il être amélioré?

MODIFIER:

Et MyEntity n'est qu'une autre classe afin de la traiter dans cette méthode générique! Ce n'est pas lié aux types T. C'est juste un argument. Mais il est clair que je ne peux pas faire ça, en utilisant 2 types comme ça. Je pensais que je pouvais assigner l'un ou l'autre et le CLR indépendamment de mon initialisation pourrait réagir comme je le voulais. Je vais accepter la réponse qui partage un peu plus d'informations à ce sujet.

35
Maximus Decimus

Tout d'abord, votre code qui essaie de définir deux contraintes de type sur le paramètre générique T1 ne compile pas

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

avec l'erreur suivante:

Une clause de contrainte a déjà été spécifiée pour le paramètre de type 'T1'. Toutes les contraintes d'un paramètre de type doivent être spécifiées dans une seule clause where.

Comme l'indique l'article MSDN, vous ne pouvez utiliser qu'une seule contrainte where sur chaque paramètre générique (voir http://msdn.Microsoft.com/en-us/library/bb384067.aspx ).

"Avec plusieurs paramètres de type, utilisez une clause where pour chaque paramètre de type ..."

Vous ne pouvez pas non plus mettre plusieurs noms de classe dans une seule contrainte "où". Un seul nom de classe et plusieurs interfaces.

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

Gardez à l'esprit que cette contrainte impose que le type réel que vous fournissez comme paramètre générique T1 doit être un successeur de la classe Supplier ou de la classe Supplier elle-même ET il doit implémenter les deux interfaces IContractor ET IComparable.

Dès que votre méthode accepte un objet MyEntity et que vous ne spécifiez pas sa relation avec les classes Employee et Supplier, je ne peux pas deviner comment cette classe MyEntity connaît les classes Employee et Supplier et comment cette relation vous aide.

La seule chose que je peux suggérer est de créer une interface ou une classe de base et d'hériter de vos deux classes. C'est la seule bonne raison que je vois pour créer une méthode générique. Cela pourrait ressembler à ceci:

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

Si vos classes Supplier et Employee n'ont pas quelque chose d'important en commun qui soit suffisant pour créer une interface commune qu'ils pourraient implémenter, vous ne devriez pas faire une méthode générique pour les traiter.

Créez une méthode surchargée pour chacun de ces types.

Imaginez que vous ayez deux classes: Wife et Wine. Les deux ont un attribut de Age et du même type également. Mais ne pensez même pas à créer une interface commune IAged pour ces classes. L'essence des classes et la signification de Age sont si différentes qu'il ne faut jamais les unifier. Néanmoins, une logique commune pourrait parfaitement vous servir. Par exemple:

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

private double AgeQualify(Wine someWine)
{
    return someWine.Age / someWine.Sugar;
}
43
Zverev Evgeniy

Vous devez soit créer des versions distinctes:

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

ou besoin de leur faire partager une classe de 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;
}

Je préfère vraiment les versions séparées, car avec elles, elles ne peuvent pas l'appeler avec Supplier et EmployeeDeparment (ou vice versa)

13
It'sNotALie.

Vous ne devriez vraiment pas utiliser de génériques dans ce cas. Il n'y a que deux options.

Alors:

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

Ce que vous pouvez faire est d'unifier les deux avec une classe de base concentrant toutes les méthodes courantes.

Dites, si le fournisseur et l'employé ont tous deux 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
}

Et puis, la méthode prend BaseClass:

private string ConcatenateText(BaseClass Entity) 
{ 
    //code
}
1
Daniel Möller