제네릭 유형의 값을 비교하는 방법은 무엇입니까?


81

제네릭 유형의 값을 어떻게 비교합니까?

최소한의 샘플로 줄였습니다.

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), 우리는 여전히 연산자 중 하나를 적용 할 수 없습니다 <, >, <=, >=, ==또는 !=. (I는 관련된 그 해결 방법을 알고 Equals()존재를 ==하고 !=있지만, 관계 연산자에 대한 도움말을하지 않습니다).

따라서 두 가지 질문이 있습니다.

  1. 이 이상한 행동을 관찰하는 이유무엇 입니까? 어떻게하는 일반적인 유형의 값 비교에서 우리를 유지 알려진 수를 IComparable? 일반 제약의 전체 목적을 어떻게 든 무너 뜨리지 않습니까?
  2. 이 문제를 해결하거나 최소한 해결하려면 어떻게해야합니까?

(이 겉보기에 간단한 문제와 관련된 몇 가지 질문이 이미 있다는 것을 알고 있지만 스레드 중 어느 것도 철저하거나 실행 가능한 답변을 제공하지 않으므로 여기에 있습니다.)

답변:


96

IComparable>=연산자에 과부하가 걸리지 않습니다 . 당신은 사용해야합니다

value.CompareTo(_minimumValue) >= 0

7
훌륭합니다. 이것은 작동합니다 (물론 설명합니다). 감사합니다! 그러나 약간 불만족스럽고 질문을 남깁니다. IComparable이 비교 연산자를 오버로드하지 않는 이유무엇 입니까? 이것은 합당한 이유가있는 의식적이고 고의적 인 디자인 결정입니까? 아니면 프레임 워크 디자인에서 간과 된 것입니까? 결국 'x.CompareTo (y)> = 0'은 'x> = y'보다 가독성이 떨어집니다.
gstercken

나는 확실히 당신의 요점을 얻습니다. 문제는 연산자가 정적이므로 인터페이스에 맞지 않는다는 것입니다. 이것이 좋은 선택인지 아닌지는 판단하지 않겠지 만, 유형이 원시적이지 않을 때 고유 한 이름을 가진 메소드가 연산자보다 읽기 쉽다고 생각하는 경향이 있습니다. 그래도 이것은 맛의 문제입니다.
faester

5
@gstercken : IComparable비교 연산자 오버로딩의 한 가지 문제 는 X.Equals(Y)false X.CompareTo(Y)를 반환해야 하지만 0을 반환 해야하는 상황이 있다는 것입니다 (요소가 다른 요소보다 크지 않음을 암시) [예 : an ExpenseItem은에 대해 자연스러운 순서를 가질 TotalCost수 있으며 비용이 동일한 비용 항목에 대한 자연 주문은 없지만, $ 3,141.59 비용이 드는 모든 비용 항목이 같은 비용이 드는 다른 모든 항목과 동일한 것으로 간주되어야한다는 의미는 아닙니다.
supercat 2013-04-15

1
@gstercken : 근본적 ==으로 논리적으로 의미 할 수있는 것들이 많이 있습니다. 어떤 상황에서는는 X==Y참일 수 있지만 거짓 일 수도 있고 X.Equals(Y), 다른 상황에서는 거짓 일 X==Y수도 있고 X.Equals(Y)참일 수도 있습니다 . 연산자 인터페이스, 과부하 과부하 될 수있다하더라도 <, <=, >>=측면에 IComparable<T>있다는 인상을 줄 수도 있습니다 ==!=또한 측면에서 오버로드 될 수 있습니다. C #이 있던 경우에, 비주얼 베이직처럼 사용 허용 ==... 그것은 힘이 나쁘지 않았을 것으로 오버로드되지 않은 클래스 유형에 대한,하지만
supercat

2
... alas C #은이 토큰을 사용하여 ==오버로드 가능한 같음 연산자와 오버로드 할 수없는 참조 같음 테스트를 모두 나타내 기로 결정했습니다 .
supercat 2013-04-15

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);
}

왜 그들이 이것을 허용하지 않았는지 모르겠지만 언어 정의가 복잡하고 사용자가 올바르게 구현하기 어려울 것이라고 생각합니다.

또는 디자이너가 남용 가능성을 좋아하지 않았습니다. 예를 들어, 일을 상상 >=A의 비교 class MagicMrMeow. 또는 class Matrix<T>. 결과는 두 값에 대해 무엇을 의미합니까?; 특히 모호성이있을 수있는 경우?

공식적인 해결 방법

위의 인터페이스는 합법적이지 않으므로 문제를 해결할 수있는 IComparable<T>인터페이스가 있습니다. 연산자를 구현하지 않고 하나의 메서드 만 노출합니다.int CompareTo(T other);

http://msdn.microsoft.com/en-us/library/4d7sx9hd.aspx 참조

int결과 (a 유사한 실제로 트리 비트 또는 트라이 어림이다 Boolean하지만 세 가지 상태로). 이 표는 결과의 의미를 설명합니다.

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

2
아, 맞아요. C #의 인터페이스가 연산자를 오버로드 할 수 없다는 것을 잊었습니다.
gstercken 2011-06-25

32

valuenull 일 수 있으면 현재 답변이 실패 할 수 있습니다. 대신 다음과 같이 사용하십시오.

Comparer<T>.Default.Compare(value, _minimumValue) >= 0

1
팁 고마워. 내가 작업하고 있던 일부 확장 방법을 위해 이것이 필요했습니다. 아래를 참조하십시오.
InteXX

1
알겠습니다. 나는 제약되지 않았습니다 TIComparable. 그러나 당신의 팁은 그럼에도 불구하고 나를 고비에 넘겼습니다.
InteXX

6
public bool IsInRange(T value) 
{
    return (value.CompareTo(_minimumValue) >= 0);
}

IComparable 제네릭으로 작업 할 때보다 작거나 큰 연산자는 모두 CompareTo 호출로 변환해야합니다. 어떤 연산자를 사용하든 동일한 순서로 비교되는 값을 유지하고 0과 비교하십시오. ( x <op> y해진다 x.CompareTo(y) <op> 0<op>이다 >, >=등)

또한 사용하는 일반 제약 조건은 where T : IComparable<T>. IComparable은 그 자체로 객체를 어떤 것과도 비교할 수 있음을 의미하며, 동일한 유형의 다른 객체와 비교하는 것이 더 적절할 수 있습니다.


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; }
}

구현해야하는 Comparer일반 제약 조건이 이미있을 때 a를 사용하는 방법을 소개합니다 . TIComparable
Fredrik Mörk

@Fredrik 일반 제약 조건이 구축되는 경향이 있습니다. 여기서 생략하는 데 동의합니다.
Marc Gravell

2

다른 사람들이 언급했듯이 CompareTo 메서드를 명시 적으로 사용해야합니다. 연산자와 함께 인터페이스를 사용할 수없는 이유는 클래스가 명확한 순위없이 임의의 수의 인터페이스를 구현할 수 있기 때문입니다. "a = foo + 5;"라는 표현을 계산하려한다고 가정 해 보겠습니다. foo가 6 개의 인터페이스를 구현할 때 모두 정수 두 번째 인수로 연산자 "+"를 정의합니다. 운영자에게 어떤 인터페이스를 사용해야합니까?

클래스가 여러 인터페이스를 파생 할 수 있다는 사실은 인터페이스를 매우 강력하게 만듭니다. 안타깝게도, 실제로하고 싶은 일에 대해 더 분명하게해야하는 경우가 많습니다.


1
MI는 일반 메서드와 마찬가지로 오버로드 된 연산자에 더 이상 문제가되지 않는다고 생각합니다. 메소드에 대한 재미있는 구문입니다. 따라서 인터페이스의 일반 메서드와 동일한 규칙을 사용하여 문제를 해결할 수 있습니다. C # 디자인 목표 중 하나는 C ++ 프로그래머에게 어느 정도 익숙해 지도록하는 것이었고 오버로드 된 연산자는 지옥에 오용되어 해당 언어로 돌아 왔습니다. 나는 디자이너가 그 방법의 의도에 대해 일종의 문서를 제공하도록 강요하는 명명 된 방법을 선호한다고 생각합니다.
Merlyn Morgan-Graham


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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.