web-dev-qa-db-ja.com

ジェネリック型の値を比較する方法は?

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

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

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. なぜこの奇妙な振る舞いを観察しますか? knownであるジェネリック型の値をIComparableと比較できないのはなぜですか?ジェネリック制約の目的全体を何らかの形で無効にしないのですか?
  2. どうすれば解決できますか、少なくとも回避できますか?

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

69
gstercken

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

value.CompareTo(_minimumValue) >= 0
85
faester

演算子のオーバーロードの問題

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

_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
_
30

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

Comparer<T>.Default.Compare(value, _minimumValue) >= 0
21
Peter Hedberg
_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自体は、オブジェクトをあらゆるものと比較できることを意味し、オブジェクトを同じタイプの他のオブジェクトと比較する方がおそらく適切です。

6
David Yaw

の代わりに value >= _minimValueComparerクラスを使用:

public bool IsInRange(T value ) {
    var result = Comparer<T>.Default.Compare(value, _minimumValue);
    if ( result >= 0 ) { return true; }
    else { return false; }
}
3
TcKs

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

クラスが複数のインターフェースを導出できるという事実は、インターフェースを非常に強力にします。残念ながら、実際に何をしたいのかをより明確にする必要がしばしばあります。

2
supercat

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

1

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
_

-EDIT-

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

_<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
_
0
InteXX