web-dev-qa-db-ja.com

ジェネリックメソッドマルチプル(OR)タイプの制約

this を読んで、メソッドを汎用メソッドにすることにより、メソッドが複数のタイプのパラメーターを受け入れることができることを学びました。この例では、「U」がIEnumerable<T>であることを確認するために、次のコードを型制約とともに使用しています。

public T DoSomething<U, T>(U arg) where U : IEnumerable<T>
{
    return arg.First();
}

次のような複数の型制約を追加できるコードがいくつか見つかりました。

public void test<T>(string a, T arg) where T: ParentClass, ChildClass 
{
    //do something
}

ただし、このコードは、argParentClassandChildClassの両方のタイプでなければならないことを強制するように見えます。私がやりたいことは、次のようにargがParentClassorChildClassのタイプになる可能性があるということです。

public void test<T>(string a, T arg) where T: string OR Exception
{
//do something
}

あなたの助けはいつものように感謝しています!

114
Mansfield

それは不可能です。ただし、特定のタイプのオーバーロードを定義できます。

public void test(string a, string arg);
public void test(string a, Exception arg);

それらがジェネリッククラスの一部である場合、メソッドのジェネリックバージョンよりも優先されます。

56
Botz3000

ボッツの答えは100%正しいです、ここに簡単な説明があります:

メソッドを(一般的であるかどうかに関係なく)作成し、そのメソッドが取るパラメーターの型を宣言するとき、コントラクトを定義します。

タイプTが行う方法を知っていることのセットを行う方法を知っているオブジェクトを私に与えると、「a」:私が宣言するタイプの戻り値、または「b」:そのタイプ。

(orを使用して)一度に複数のタイプを指定しようとしたり、コントラクトがファジーになる複数のタイプである可能性のある値を返すように試行した場合:

ロープをジャンプする方法を知っているか、15桁までのpiを計算する方法を知っているオブジェクトを私に与えたら、釣りに行くか、コンクリートを混ぜることができるオブジェクトを返します。

問題は、メソッドに入ると、IJumpRopeまたはPiFactoryを与えられたかどうかわからないことです。さらに、メソッドを使用して(魔法のようにコンパイルしたと仮定して)使用すると、FisherまたはAbstractConcreteMixerがあるかどうかが本当にわかりません。基本的に、それは全体の方法をより混乱させます。

問題の解決策は、次の2つの可能性のいずれかです。

  1. 考えられる各変換、動作などを定義する複数のメソッドを定義します。それがボッツの答えです。プログラミングの世界では、これはメソッドのオーバーロードと呼ばれます。

  2. メソッドに必要なすべてのことを行う方法を知っている基本クラスまたはインターフェースを定義し、1つのメソッドにjustそのタイプを設定させます。これには、小さなクラスでstringExceptionをラップして実装にマッピングする方法を定義することが含まれますが、すべてが非常に明確で読みやすいです。 4年後に来て、あなたのコードを読み、何が起こっているのかを簡単に理解することができました。

どちらを選択するかは、選択1と2がどれほど複雑で、どれだけ拡張できるかによって異なります。

あなたの特定の状況のた​​めに、私はあなたが例外からメッセージまたは何かを引き出していることを想像するつもりです:

public interface IHasMessage
{
    string GetMessage();
}

public void test(string a, IHasMessage arg)
{
    //Use message
}

必要なのは、stringExceptionをIHasMessageに変換するメソッドだけです。非常に簡単。

24
Crisfole

ChildClassがParentClassから派生したことを意味する場合、ParentClassとChildClassの両方を受け入れるために次のように書くだけです。

public void test<T>(string a, T arg) where T: ParentClass 
{
    //do something
}

一方、継承関係のない2つの異なる型を使用する場合は、同じインターフェイスを実装する型を検討する必要があります。

public interface ICommonInterface
{
    string SomeCommonProperty { get; set; }
}

public class AA : ICommonInterface
{
    public string SomeCommonProperty
    {
        get;set;
    }
}

public class BB : ICommonInterface
{
    public string SomeCommonProperty
    {
        get;
        set;
    }
}

次に、汎用関数を次のように記述できます。

public void Test<T>(string a, T arg) where T : ICommonInterface
{
    //do something
}
7
daryal