web-dev-qa-db-ja.com

複数のクラスを持つジェネリック

私は物事を単純化するためにこの汎用メソッドを作成しようとしていますが、私はそれを台無しにしたと思います!私の問題を解決できますか?

これはコンパイルします:

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

これはコンパイルされませんが:

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

メッセージ:タイプサプライヤーは、ジェネリックタイプまたはメソッドConcatenateText(MyEntity myEntity)のタイプパラメーターT1として使用できません。サプライヤーから従業員への暗黙の参照変換はありません

これはできますか?何が間違っていますか?改善できますか?

編集:

そして、MyEntityは、この汎用メソッド内で処理するための単なる別のクラスです! T型とは関係ありません。単なる引数です。しかし、そのような2つのタイプを使用して、それができないことは明らかです。私はどちらかを割り当てることができ、私の初期化とは無関係にCLRが思い通りに反応できると思った。私はそれについてもう少し情報を共有する答えを受け入れます。

35
Maximus Decimus

まず、ジェネリックパラメーターT1に2つの型制約を設定しようとするコードはコンパイルされません。

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

次のエラーで:

型パラメーター 'T1'に制約句が既に指定されています。型パラメーターのすべての制約は、単一のwhere句で指定する必要があります。

MSDNの記事にあるように、各ジェネリックパラメーターで使用できるwhere制約は1つだけです( http://msdn.Microsoft.com/en-us/library/bb384067.aspx を参照)。

「複数の型パラメーターを使用する場合、各型パラメーターに対して1つのwhere句を使用してください...」

また、複数のクラス名を1つの「where」制約に入れることはできません。 1つのクラス名と複数のインターフェースのみ。

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

この制約は、ジェネリックパラメーターT1として提供する実際の型は、SupplierクラスまたはSupplierクラス自体の後継である必要があり、IContractorIComparableの両方のインターフェイスを実装する必要があることに注意してください。

メソッドがMyEntityオブジェクトを受け入れ、EmployeeクラスとSupplierクラスとの関係を指定しないとすぐに、このMyEntityクラスがEmployeeクラスとSupplierクラスをどのように認識し、この関係がどのように役立つかを推測できません。

私が提案できる唯一のことは、インターフェースまたは基本クラスを作成し、そこから両方のクラスを継承することです。これが、汎用メソッドを作成するための唯一の正当な理由です。次のようになります。

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

クラスSupplierとEmployeeに、実装可能な共通インターフェースを作成するのに十分な重要な共通点がない場合、それらを処理するための汎用メソッドを作成しないでください。

そのようなタイプごとにオーバーロードメソッドを作成します。

WifeWineの2つのクラスがあるとします。両方ともAgeと同じ型の属性を持っています。しかし、それらのクラスに共通のインターフェイスIAgedを作成することさえ考えないでください。クラスの本質とAgeの意味は非常に異なるため、クラスを統一することはできません。それにもかかわらず、いくつかの一般的なロジックが完全に役立つかもしれません。例えば:

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

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

個別のバージョンを作成する必要があります。

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

または、基本クラスを共有させる必要があります。

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

私は別々のバージョンを好むでしょう、本当に彼らはそれをSupplierEmployeeDeparment(またはその逆)で呼び出すことができないからです

13
It'sNotALie.

その場合、ジェネリックを使用しないでください。選択肢は2つだけです。

そう:

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

できることは、両方の基本クラスをすべての一般的なメソッドに集中させることです。

サプライヤと従業員の両方に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
}

そして、メソッドはBaseClassを取ります:

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