163

これを読んで、メソッドをジェネリックメソッドにすることで、複数のタイプのパラメーターを受け入れることができることを学びました。この例では、「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
}

ただし、このコードは、とargの両方のタイプである必要があることを強制しているように見えます。私がやりたいのは、argは次のタイプまたは次の方法である可能性があるということです。ParentClass ChildClassParentClass ChildClass

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

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

4

4 に答える 4

76

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

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

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

于 2012-05-31T12:50:56.023 に答える
32

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

メソッド(汎用かどうかに関係なく)を記述し、メソッドが使用するパラメーターのタイプを宣言する場合、コントラクトを定義します。

タイプTが実行方法を知っている一連のことを実行する方法を知っているオブジェクトを私に与えると、「a」:宣言したタイプの戻り値、または「b」:を使用するある種の動作のいずれかを提供できます。そのタイプ。

一度に複数のタイプを指定しようとした場合(またはを使用して)、またはコントラクトがあいまいになる複数のタイプである可能性のある値を返すように取得しようとした場合:

縄跳びの方法や円周率の計算方法を知っているオブジェクトを15桁目に与えてくれたら、釣りに行くことができるオブジェクトか、コンクリートを混ぜるオブジェクトを返します。

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

問題の解決策は、次の2つの可能性のうちの1つです。

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

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

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

したがって、特定の状況では、例外からメッセージなどを引き出しているだけだと想像します。

public interface IHasMessage
{
    string GetMessage();
}

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

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

于 2012-05-31T13:03:52.317 に答える
10

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
}
于 2012-05-31T12:55:16.820 に答える
7

この質問と同じくらい古いので、私はまだ上記の私の説明にランダムな賛成票を受け取ります。説明はそのままでも完全に問題ありませんが、共用体型の代わりとして役立つ型(C#で直接サポートされていない質問に対する強い型の回答)でもう一度答えます。 )。

using System;
using System.Diagnostics;

namespace Union {
    [DebuggerDisplay("{currType}: {ToString()}")]
    public struct Either<TP, TA> {
        enum CurrType {
            Neither = 0,
            Primary,
            Alternate,
        }
        private readonly CurrType currType;
        private readonly TP primary;
        private readonly TA alternate;

        public bool IsNeither => currType == CurrType.Neither;
        public bool IsPrimary => currType == CurrType.Primary;
        public bool IsAlternate => currType == CurrType.Alternate;

        public static implicit operator Either<TP, TA>(TP val) => new Either<TP, TA>(val);

        public static implicit operator Either<TP, TA>(TA val) => new Either<TP, TA>(val);

        public static implicit operator TP(Either<TP, TA> @this) => @this.Primary;

        public static implicit operator TA(Either<TP, TA> @this) => @this.Alternate;

        public override string ToString() {
            string description = IsNeither ? "" :
                $": {(IsPrimary ? typeof(TP).Name : typeof(TA).Name)}";
            return $"{currType.ToString("")}{description}";
        }

        public Either(TP val) {
            currType = CurrType.Primary;
            primary = val;
            alternate = default(TA);
        }

        public Either(TA val) {
            currType = CurrType.Alternate;
            alternate = val;
            primary = default(TP);
        }

        public TP Primary {
            get {
                Validate(CurrType.Primary);
                return primary;
            }
        }

        public TA Alternate {
            get {
                Validate(CurrType.Alternate);
                return alternate;
            }
        }

        private void Validate(CurrType desiredType) {
            if (desiredType != currType) {
                throw new InvalidOperationException($"Attempting to get {desiredType} when {currType} is set");
            }
        }
    }
}

上記のクラスは、TPまたはTAのいずれかであるタイプを表します。あなたはそれをそのように使うことができます(タイプは私の元の答えを参照しています):

// ...
public static Either<FishingBot, ConcreteMixer> DemoFunc(Either<JumpRope, PiCalculator> arg) {
  if (arg.IsPrimary) {
    return new FishingBot(arg.Primary);
  }
  return new ConcreteMixer(arg.Secondary);
}

// elsewhere:

var fishBotOrConcreteMixer = DemoFunc(new JumpRope());
var fishBotOrConcreteMixer = DemoFunc(new PiCalculator());

重要な注意事項:

  • IsPrimary最初にチェックしないと、ランタイムエラーが発生します。
  • IsNeither IsPrimaryまたはのいずれかを確認できますIsAlternate
  • Primaryとを介して値にアクセスできますAlternate
  • TP /TAとEither<TP、TA>の間には暗黙のコンバーターがあり、値またはEither期待される場所に値を渡すことができます。またはが期待される場所を渡した、に間違ったタイプの値が含まれている場合、実行時エラーが発生します。EitherTATPEither

私は通常、メソッドが結果またはエラーのいずれかを返すようにしたい場合にこれを使用します。それは本当にそのスタイルコードをクリーンアップします。また、非常にまれに(めったに)これをメソッドのオーバーロードの代わりに使用します。現実的には、これはそのような過負荷の非常に貧弱な代替手段です。

于 2019-11-22T15:16:51.870 に答える