Generic with multiple classes

C#GenericsTypesParameters

C# Problem Overview


I'm trying to create this generic method to simplify things but I think I messed it up! Can you help with my problem?

This compiles:

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

While this does not compile:

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: The type Supplier cannot be used as type parameter T1 in the generic type or method ConcatenateText(MyEntity myEntity). There is no implicit reference conversion from Supplier to Employee

Can this be done? What am I doing wrong? Can it be improved?

EDIT:

And MyEntity is just another class in order to process it inside this generic method! It's not related to the types T. It just an argument. But it's clear that I can't do that, using 2 types like that. I thought that I could assign one or another and the CLR independently of my initialization could react as I wanted. I gonna accept the answer who share a little bit more information about it.

C# Solutions


Solution 1 - C#

First of all, your code that tries to set two type constraints on generic parameter T1 does not compile

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

with the following error:

> 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.

As MSDN article states you can use only one where constraint on each generic parameter (see http://msdn.microsoft.com/en-us/library/bb384067.aspx).

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

You also cannot put multiple class names into one 'where' constraint. Only one class name and several interfaces.

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

Keep in mind that this constraint dictates that the actual type you provide as the generic parameter T1 must be a successor of the Supplier class or Supplier class itself AND it has to implement both IContractor AND IComparable interfaces.

As soon as your method accepts a MyEntity object and you do not specify what relation it has to Employee and Supplier classes, I cannot guess how this MyEntity class knows about Employee and Supplier classes and how this relation helps you.

The only thing I can suggest is either to create an interface or a base class and inherit both of your classes from it. This is the only good reason I see for creating a generic method. It could look like this:

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

If your classes Supplier and Employee do not have something important in common that is enough for creating a common interface they could implement then you should not make a generic method for processing them.

Create an overloaded method for each of such types.

Imagine you have two classes: Wife and Wine. Both have an attribute of Age and of the same type too. But do not even think of creating a common interface IAged for those classes. The essence of the classes and the meaning of the Age is so different that one should never unify them. Nevertheless some common logic might perfectly serve you. For example:

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

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

Solution 2 - C#

You either need to make separate versions:

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

or need to make them share a base class:

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

I'd prefer the separate versions, really, because with them they can't call it with Supplier and EmployeeDeparment (or vice versa)

Solution 3 - C#

You really should not use generics in that case. There are only two options.

So:

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

What you can do is unify both with a base class concentrating all common methods.

Say, if both supplier and employee has 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
}

And then, the method takes BaseClass:

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMaximus DecimusView Question on Stackoverflow
Solution 1 - C#Zverev EvgeniyView Answer on Stackoverflow
Solution 2 - C#It'sNotALie.View Answer on Stackoverflow
Solution 3 - C#Daniel MöllerView Answer on Stackoverflow