90

ジェネリック型の値を比較するにはどうすればよいですか?

私はそれを最小限のサンプルに減らしました:

public class Foo<T> where T : IComparable
{
    private T _minimumValue = default(T);

    public bool IsInRange(T value) 
    {
        return (value >= _minimumValue); // <-- Error here
    }
}

エラーは次のとおりです。

演算子'>='は、タイプ'T'および'T'のオペランドには適用できません。

一体全体!?Tはすでにに制約されてIComparableおり、値型()に制約されている場合でも、演算子where T: struct、、、、、またはを適用することはできません。(とに関連する回避策が存在することは知っていますが、関係演算子には役立ちません)。<><=>===!=Equals()==!=

したがって、2つの質問:

  1. なぜこの奇妙な行動を観察するのですか?既知のジェネリック型の値を比較できない理由は何ですかIComparable?それはどういうわけか一般的な制約の目的全体を打ち負かしませんか?
  2. これを解決するにはどうすればよいですか、または少なくとも回避するにはどうすればよいですか?

(この一見単純な問題に関連する質問がすでにいくつかあることを認識していますが、どのスレッドも網羅的または実行可能な答えを提供していないので、ここにあります。)

4

8 に答える 8

101

IComparable>=演算子をオーバーロードしません。あなたは使用する必要があります

value.CompareTo(_minimumValue) >= 0
于 2011-06-25T21:18:18.203 に答える
36

valuenullになる可能性がある場合、現在の回答は失敗する可能性があります。代わりに次のようなものを使用してください。

Comparer<T>.Default.Compare(value, _minimumValue) >= 0
于 2014-11-07T15:18:07.557 に答える
35

演算子のオーバーロードに関する問題

残念ながら、インターフェースにオーバーロードされた演算子を含めることはできません。コンパイラでこれを入力してみてください。

public interface IInequalityComaparable<T>
{
    bool operator >(T lhs, T rhs);
    bool operator >=(T lhs, T rhs);
    bool operator <(T lhs, T rhs);
    bool operator <=(T lhs, T rhs);
}

なぜ許可されなかったのかはわかりませんが、言語の定義が複雑で、ユーザーが正しく実装するのは難しいと思います。

それか、デザイナーは虐待の可能性を気に入らなかった。たとえば、で>=比較を行うことを想像してくださいclass MagicMrMeow。またはclass Matrix<T>。結果は2つの値についてどういう意味ですか?; 特にあいまいさが生じる可能性がある場合はどうでしょうか。

公式の回避策

上記のインターフェースは合法ではないためIComparable<T>、問題を回避するためのインターフェースがあります。演算子を実装せず、1つのメソッドのみを公開します。int CompareTo(T other);

http://msdn.microsoft.com/en-us/library/4d7sx9hd.aspxを参照してください

結果は、int実際には3ビットまたは3ナリーです(に似ていますBooleanが、3つの状態があります)。この表は、結果の意味を説明しています。

Value              Meaning

Less than zero     This object is less than
                   the object specified by the CompareTo method.

Zero               This object is equal to the method parameter.

Greater than zero  This object is greater than the method parameter.

回避策の使用

と同等のことを行うには、value >= _minimumValue代わりに次のように記述する必要があります。

value.CompareTo(_minimumValue) >= 0
于 2011-06-25T21:33:51.230 に答える
7
public bool IsInRange(T value) 
{
    return (value.CompareTo(_minimumValue) >= 0);
}

IComparableジェネリックを使用する場合、すべてのより小さい/より大きい演算子をCompareToの呼び出しに変換する必要があります。どの演算子を使用する場合でも、値を同じ順序で比較し、ゼロと比較します。(x <op> yになりますx.CompareTo(y) <op> 0、ここで<op>、、>など>=

また、使用する一般的な制約はであることが推奨されますwhere T : IComparable<T>。IComparable自体は、オブジェクトを任意のものと比較できることを意味します。オブジェクトを同じタイプの他のオブジェクトと比較する方がおそらく適切です。

于 2011-06-25T21:18:03.117 に答える
3

クラスをvalue >= _minimValue使用する代わりに:Comparer

public bool IsInRange(T value ) {
    var result = Comparer<T>.Default.Compare(value, _minimumValue);
    if ( result >= 0 ) { return true; }
    else { return false; }
}
于 2011-06-25T21:19:53.127 に答える
2

他の人が述べているように、CompareToメソッドを明示的に使用する必要があります。演算子でインターフェースを使用できない理由は、クラスが任意の数のインターフェースを実装する可能性があり、それらの間の明確なランク付けがないためです。式「a=foo+5;」を計算しようとしたとします。fooが6つのインターフェースを実装した場合、そのすべてが整数の2番目の引数を持つ演算子「+」を定義します。オペレーターはどのインターフェースを使用する必要がありますか?

クラスが複数のインターフェースを派生させることができるという事実は、インターフェースを非常に強力にします。残念ながら、それはしばしば、実際に何をしたいのかについてより明確にすることを強制します。

于 2011-06-25T21:58:31.553 に答える
1

IComparableと呼ばれる関数のみを強制しますCompareTo()。したがって、あなたが言及した演算子のいずれも適用することはできません

于 2011-06-25T21:19:25.443 に答える
0

Peter Hedburgの答えを使用して、ジェネリックスのオーバーロードされた拡張メソッドを作成することができました。CompareToタイプTが不明であり、そのインターフェイスを提示しないため、このメソッドはここでは機能しないことに注意してください。そうは言っても、私は他の選択肢を見たいと思っています。

C#で投稿したいのですが、Telerikのコンバーターがこのコードで失敗します。私はC#に精通していないため、手動で確実に変換できません。誰かが名誉をやりたいのなら、私はこれがそれに応じて編集されるのを見てうれしいです。

<Extension>
<DebuggerStepThrough>
Public Sub RemoveDuplicates(Of T)(Instance As List(Of T))
  Instance.RemoveDuplicates(Function(X, Y) Comparer(Of T).Default.Compare(X, Y))
End Sub



<Extension>
<DebuggerStepThrough>
Public Sub RemoveDuplicates(Of T)(Instance As List(Of T), Comparison As Comparison(Of T))
  Instance.RemoveDuplicates(New List(Of Comparison(Of T)) From {Comparison})
End Sub



<Extension>
<DebuggerStepThrough>
Public Sub RemoveDuplicates(Of T)(Instance As List(Of T), Comparisons As List(Of Comparison(Of T)))
  Dim oResults As New List(Of Boolean)

  For i As Integer = 0 To Instance.Count - 1
    For j As Integer = Instance.Count - 1 To i + 1 Step -1
      oResults.Clear()

      For Each oComparison As Comparison(Of T) In Comparisons
        oResults.Add(oComparison(Instance(i), Instance(j)) = 0)
      Next oComparison

      If oResults.Any(Function(R) R) Then
        Instance.RemoveAt(j)
      End If
    Next j
  Next i
End Sub

- 編集 -

OPで示されているように、すべてのメソッドに制約Tすることで、これをクリーンアップすることができました。IComparable(Of T)この制約にはT、実装するための型IComparable(Of <type>)も必要であることに注意してください。

<Extension>
<DebuggerStepThrough>
Public Sub RemoveDuplicates(Of T As IComparable(Of T))(Instance As List(Of T))
  Instance.RemoveDuplicates(Function(X, Y) X.CompareTo(Y))
End Sub
于 2017-12-14T06:42:17.477 に答える