372

MSDN==のオペレーターのドキュメントによると、

事前定義された値型の場合、等価演算子(==)は、オペランドの値が等しい場合はtrueを返し、そうでない場合はfalseを返します。文字列以外の参照型の場合、2つのオペランドが同じオブジェクトを参照している場合、==はtrueを返します。文字列タイプの場合、==は文字列の値を比較します。ユーザー定義の値型は、==演算子をオーバーロードする可能性があります(演算子を参照)。したがって、ユーザー定義の参照型も可能ですが 、デフォルトでは、==は、事前定義された参照型とユーザー定義の参照型の両方について上記のように動作します。

では、なぜこのコードスニペットはコンパイルに失敗するのでしょうか。

bool Compare<T>(T x, T y) { return x == y; }

エラー演算子'=='はタイプ'T'および'T'のオペランドには適用できません。私が理解している限り、==演算子はすべてのタイプに対して事前定義されているので、なぜだろうか?

編集:ありがとう、みんな。最初は、ステートメントが参照型のみに関するものであることに気づきませんでした。また、すべての値型に対してビットごとの比較が提供されていると思いましたが、これは正しくないことがわかりました。

しかし、参照型を使用している場合、==演算子は事前定義された参照比較を使用しますか、それとも型が定義されている場合はオーバーロードされたバージョンの演算子を使用しますか?

編集2:試行錯誤の==結果、無制限のジェネリック型を使用する場合、オペレーターは事前定義された参照比較を使用することがわかりました。実際、コンパイラーは、制限された型の引数に対して見つけることができる最良の方法を使用しますが、それ以上探すことはありません。たとえば、以下のコードは、が呼び出されtrueた場合でも常に出力されます。Test.test<B>(new B(), new B())

class A { public static bool operator==(A x, A y) { return true; } }
class B : A { public static bool operator==(B x, B y) { return false; } }
class Test { void test<T>(T a, T b) where T : A { Console.WriteLine(a == b); } }
4

13 に答える 13

329

他の人が言ったように、 T が参照型に制約されている場合にのみ機能します。制約がなければ、null と比較できますが、null のみです。その比較は、null 非許容型の場合は常に false になります。

Equals を呼び出す代わりに、IComparer<T>- を使用することをお勧めします。それ以上の情報がない場合EqualityComparer<T>.Defaultは、これが適切な選択です。

public bool Compare<T>(T x, T y)
{
    return EqualityComparer<T>.Default.Equals(x, y);
}

他のことは別として、これによりボクシング/キャスティングが回避されます。

于 2008-12-24T07:32:38.407 に答える
152

「...デフォルトで == は、事前定義された参照型とユーザー定義の参照型の両方について、上記のように動作します。」

型 T は必ずしも参照型ではないため、コンパイラはその仮定を行うことができません。

ただし、これはより明示的であるためコンパイルされます。

    bool Compare<T>(T x, T y) where T : class
    {
        return x == y;
    }

追加の質問「しかし、参照型を使用している場合、== 演算子は定義済みの参照比較を使用しますか?それとも、型が定義されている場合は、オーバーロードされたバージョンの演算子を使用しますか?」

Generics の == はオーバーロードされたバージョンを使用すると考えていましたが、次のテストはそうではないことを示しています。興味深い...理由が知りたいです!誰かが知っているなら、共有してください。

namespace TestProject
{
 class Program
 {
    static void Main(string[] args)
    {
        Test a = new Test();
        Test b = new Test();

        Console.WriteLine("Inline:");
        bool x = a == b;
        Console.WriteLine("Generic:");
        Compare<Test>(a, b);

    }


    static bool Compare<T>(T x, T y) where T : class
    {
        return x == y;
    }
 }

 class Test
 {
    public static bool operator ==(Test a, Test b)
    {
        Console.WriteLine("Overloaded == called");
        return a.Equals(b);
    }

    public static bool operator !=(Test a, Test b)
    {
        Console.WriteLine("Overloaded != called");
        return a.Equals(b);
    }
  }
}

出力

インライン: オーバーロード == 呼び出し

ジェネリック:

何かキーを押すと続行します 。. .

フォローアップ 2

比較方法を次のように変更することを指摘したいと思います

    static bool Compare<T>(T x, T y) where T : Test
    {
        return x == y;
    }

オーバーロードされた == 演算子が呼び出されます。タイプを(whereとして)指定しないと、コンパイラはオーバーロードされた演算子を使用する必要があると推測できません...ただし、タイプを指定しなくても、その決定を下すのに十分な情報があると思います。

于 2008-12-24T06:44:55.227 に答える
45

一般に、を実装するもの、または適切な実装をEqualityComparer<T>.Default.Equals持つもので仕事をする必要があります。IEquatable<T>Equals

ただし、何らかの理由で==とが異なる方法で実装されている場合は、ジェネリック演算子に関する私の作業が役立つはずです。(特に)のオペレーターバージョンをサポートしています:Equals

  • 等しい(T値1、T値2)
  • NotEqual(T 値 1、T 値 2)
  • GreaterThan(T 値 1、T 値 2)
  • LessThan(T値1, T値2)
  • GreaterThanOrEqual(T 値 1、T 値 2)
  • LessThanOrEqual(T値1, T値2)
于 2008-12-24T10:18:15.907 に答える
37

非常に多くの答えがありますが、理由を説明するものは 1 つもありません。(ジョバンニが明示的に尋ねた)...

.NET ジェネリックは、C++ テンプレートのようには機能しません。C++ テンプレートでは、実際のテンプレート パラメーターが判明した後にオーバーロードの解決が行われます。

.NET ジェネリック (C# を含む) では、オーバーロードの解決は、実際のジェネリック パラメーターを知らなくても発生します。呼び出す関数を選択するためにコンパイラが使用できる唯一の情報は、ジェネリック パラメーターの型制約から得られます。

于 2010-12-12T20:38:04.250 に答える
13

コンパイルは、T が構造体 (値型) ではないことを認識できません。だからあなたはそれが私が思う参照型だけであることができると言わなければなりません:

bool Compare<T>(T x, T y) where T : class { return x == y; }

T が値型である可能性があるx == y場合、型に演算子 == が定義されていない場合など、不適切な形式になる場合があるためです。これについても同じことが起こります。これはより明白です。

void CallFoo<T>(T x) { x.foo(); }

関数 foo を持たない型 T を渡すことができるため、これも失敗します。C# では、考えられるすべての型が常に関数 foo を持っていることを確認する必要があります。これは、where 句によって行われます。

于 2008-12-24T06:42:44.140 に答える
9

クラスの制約がない場合は次のようになります。

bool Compare<T> (T x, T y) where T: class
{
    return x == y;
}

class演算子で制約Equalsされている間は==継承元でObject.Equalsあるのに対し、構造体の制約はオーバーライドすることを理解する必要がありますValueType.Equals

ご了承ください:

bool Compare<T> (T x, T y) where T: struct
{
    return x == y;
}

また、同じコンパイラ エラーが発生します。

値型の等価演算子の比較がコンパイラによって拒否される理由はまだわかりません。ただし、これが機能することは事実です。

bool Compare<T> (T x, T y)
{
    return x.Equals(y);
}
于 2008-12-24T06:58:03.343 に答える
6

私の場合、等価演算子の単体テストを行いたかったのです。ジェネリック型を明示的に設定せずに、等値演算子の下でコードを呼び出す必要がありました。呼び出しメソッドとしてのアドバイスEqualityComparerは役に立ちませんでしたが、等値演算子では役に立ちませんでした。EqualityComparerEquals

を構築することにより、ジェネリック型でこれを機能させる方法を次に示しますLINQ==and!=演算子の正しいコードを呼び出します。

/// <summary>
/// Gets the result of "a == b"
/// </summary>
public bool GetEqualityOperatorResult<T>(T a, T b)
{
    // declare the parameters
    var paramA = Expression.Parameter(typeof(T), nameof(a));
    var paramB = Expression.Parameter(typeof(T), nameof(b));
    // get equality expression for the parameters
    var body = Expression.Equal(paramA, paramB);
    // compile it
    var invokeEqualityOperator = Expression.Lambda<Func<T, T, bool>>(body, paramA, paramB).Compile();
    // call it
    return invokeEqualityOperator(a, b);
}

/// <summary>
/// Gets the result of "a =! b"
/// </summary>
public bool GetInequalityOperatorResult<T>(T a, T b)
{
    // declare the parameters
    var paramA = Expression.Parameter(typeof(T), nameof(a));
    var paramB = Expression.Parameter(typeof(T), nameof(b));
    // get equality expression for the parameters
    var body = Expression.NotEqual(paramA, paramB);
    // compile it
    var invokeInequalityOperator = Expression.Lambda<Func<T, T, bool>>(body, paramA, paramB).Compile();
    // call it
    return invokeInequalityOperator(a, b);
}
于 2016-09-03T09:39:35.763 に答える
4

カスタム型の演算子が確実に呼び出されるようにしたい場合は、リフレクションを介して行うことができます。ジェネリック パラメーターを使用して型を取得し、必要な演算子 (op_Equality、op_Inequality、op_LessThan など) の MethodInfo を取得するだけです。

var methodInfo = typeof (T).GetMethod("op_Equality", 
                             BindingFlags.Static | BindingFlags.Public);    

次に、MethodInfo の Invoke メソッドを使用してオペレーターを実行し、オブジェクトをパラメーターとして渡します。

var result = (bool) methodInfo.Invoke(null, new object[] { object1, object2});

これにより、オーバーロードされた演算子が呼び出され、ジェネリック パラメーターに適用された制約によって定義された演算子は呼び出されません。実用的ではないかもしれませんが、いくつかのテストを含む汎用基本クラスを使用する場合に、オペレーターの単体テストに役立つ可能性があります。

于 2011-02-18T14:25:36.787 に答える
4

このための MSDN Connect エントリがここにあります

Alex Turner の返信は次のように始まります。

残念ながら、この動作は仕様によるものであり、値の型を含む可能性のある型パラメーターで == を使用できるようにする簡単な解決策はありません。

于 2009-08-19T15:51:59.693 に答える
2

最新のmsdnを見て、次の関数を書きました。x2 つのオブジェクトとを簡単に比較できますy

static bool IsLessThan(T x, T y) 
{
    return ((IComparable)(x)).CompareTo(y) <= 0;
}
于 2013-06-20T02:03:45.813 に答える
1

bool Compare(T x, T y) where T : class { return x == y; }

ユーザー定義の参照型の場合は == が処理されるため、上記は機能します。
値型の場合、== をオーバーライドできます。その場合、「!=」も定義する必要があります。

「==」を使用した一般的な比較が許可されていないことが理由であると思います。

于 2008-12-24T06:43:28.383 に答える