랩퍼가 동일한 오브젝트를 랩핑 할 때 == 연산자를 사용하여 동일하게 비교해야합니까?


19

개발자가 XML의 특성을 쉽게 구문 분석 할 수있는 XML 요소에 대한 래퍼를 작성하고 있습니다. 랩퍼는 랩핑되는 오브젝트 이외의 상태가 없습니다.

==연산자에 대한 과부하를 포함하는 다음 구현 (이 예제에서는 단순화 됨)을 고려하고 있습니다.

class XmlWrapper
{
    protected readonly XElement _element;

    public XmlWrapper(XElement element)
    {
        _element = element;
    }

    public string NameAttribute
    {
        get
        {
            //Get the value of the name attribute
        }
        set
        {
            //Set the value of the name attribute
        }
    }

    public override bool Equals(object other)
    {
        var o = other as XmlWrapper;
        if (o == null) return false;
        return _element.Equals(o._element);
    }

    public override int GetHashCode()
    {
        return _element.GetHashCode();
    }

    static public bool operator == (XmlWrapper lhs, XmlWrapper rhs)
    {
        if (ReferenceEquals(lhs, null) && ReferenceEquals(rhs, null)) return true;
        if (ReferenceEquals(lhs, null) || ReferenceEquals(rhs, null)) return false;

        return lhs._element == rhs._element;
    }

    static public bool operator != (XmlWrapper lhs, XmlWrapper rhs)
    {
        return !(lhs == rhs);
    }
}

관용적 인 C #을 이해하는 것처럼 ==연산자는 참조 평등을위한 것이고 Equals()메소드는 값 평등을위한 것입니다. 그러나이 경우 "값"은 래핑되는 개체에 대한 참조 일뿐입니다. 따라서 C #의 기존 또는 관용적 인 것이 무엇인지 명확하지 않습니다.

예를 들어이 코드에서 ...

var underlyingElement = new XElement("Foo");
var a = new XmlWrapper(underlyingElement);
var b = new XmlWrapper(underlyingElement);

a.NameAttribute = "Hello";
b.NameAttribute = "World";

if (a == b)
{
    Console.WriteLine("The wrappers a and b are the same.");
}

.... 프로그램에서 "래퍼 a와 b가 동일해야"합니까? 아니면 이상 할 것입니다. 즉 가장 놀랍게도 교장을 위반 합니까?


모든 시간 동안 나는 Equals결코 무시하지 않았다 ==(그러나 결코 다른 방법은 아니다). 게으른 관용적입니까? 가장 놀랍게도 위반하는 명백한 캐스트없이 다른 행동을한다면.
radarbob

이에 대한 대답은 NameAttribute의 기능에 따라 다릅니다. 기본 요소를 수정합니까? 추가 데이터가 있습니까? 예제 코드의 의미 (그리고 동등한 것으로 간주되어야하는지 여부)는 이에 따라 변경되므로 작성해야합니다.
Errorsatz

@Errorsatz 사과하지만 예제를 간결하게 유지하고 싶었고 래핑 된 요소 (특히 "name"이라는 XML 특성을 수정하여)를 수정하는 것이 분명하다고 가정했지만 세부 사항은 거의 문제가되지 않습니다. 랩퍼는 랩핑 된 요소에 대한 읽기 / 쓰기 액세스를 허용하지만 자체 상태는 포함하지 않습니다.
John Wu

4
글쎄,이 경우 그들은 중요하다-후자는 전자를 덮어 쓸 것이기 때문에 "Hello"와 "World"할당이 잘못되었다는 것을 의미합니다. 이는 두 사람이 동등하다고 간주 될 수있는 Martin의 답변에 동의한다는 의미입니다. NameAttribute가 실제로 서로 다르면 동일하다고 생각하지 않습니다.
Errorsatz

2
"관용적 C #을 이해하면서 == 연산자는 참조 평등을위한 것이고 Equals () 메서드는 값 평등을위한 것입니다." 그래요? 내가 본 == 오버로드 된 대부분의 시간은 가치 평등을위한 것입니다. 가장 중요한 예는 System.String입니다.
Arturo Torres Sánchez

답변:


17

랩핑에 대한 참조 XElement는 변경할 수 없으므로 XmlWrapper동일한 요소를 랩핑하는 두 인스턴스간에 외부 적으로 관찰 가능한 차이가 없으므로이 ==사실을 반영하기 위해 과부하 하는 것이 좋습니다 .

클라이언트 코드는 거의 항상 논리적 평등에 관심이 있습니다 (기본적으로 참조 유형에 대한 참조 평등을 사용하여 구현 됨). 힙에 두 개의 인스턴스가 있다는 사실은 클라이언트가 신경 쓰지 않아야 할 구현 세부 정보이며 Object.ReferenceEquals직접 사용하는 인스턴스입니다 .


9

가장 이해가된다면

질문과 답변은 개발자의 기대 사항이며 기술 요구 사항은 아닙니다.

경우 당신이 정체성을 가지고 있고 그것의 내용으로 순수하게 정의하지에 래퍼를 고려, 다음 질문에 대한 대답은 '예'입니다.

그러나 이것은 반복되는 문제입니다. 두 개의 래퍼가 서로 다른 객체를 포장 할 때 동일한 내용을 갖는 두 객체를 모두 포함 할 때 평등해야합니까?

대답은 반복된다. 경우 내용 객체가 어떤 개인의 정체성이없는 대신 순수하게 자신의 콘텐츠에 의해 정의되며, 다음 내용 개체를 효과적으로 평등을 전시 할 예정 래퍼입니다. 그런 다음 다른 래퍼에 내용 개체를 래핑하면 해당 (추가) 래퍼도 동등해야합니다.

그것은의 거북이 끝까지 .


일반적인 팁

기본 동작에서 벗어날 때마다 명시 적으로 문서화해야합니다. 개발자는 내용이 동일하더라도 두 가지 참조 유형이 동일하지 않을 것으로 예상합니다. 해당 동작을 변경하면 모든 개발자가이 비정형 동작을 인식 할 수 있도록 명확하게 문서화하는 것이 좋습니다.


관용적 인 C #을 이해하는 것처럼 ==연산자는 참조 평등을위한 것이고 Equals()메소드는 값 평등을위한 것입니다.

이것이 기본 동작이지만 이것은 움직일 수없는 규칙이 아닙니다. 그것은 관습의 문제이지만 , 정당한 경우 관습을 변경할 수 있습니다 .

string==값 평등 검사 와 마찬가지로 문자열 인턴이없는 경우에도 훌륭한 예 입니다. 왜? 간단히 말하면, 문자열은 가치 객체처럼 동작하기 때문에 대부분의 개발자에게는 더 직관적입니다.

랩퍼가 전반적으로 가치 평등을 나타내도록하여 코드베이스 (또는 개발자의 삶)를 현저하게 단순화 할 수 있다면 그것을 진행하십시오 (그러나 문서화하십시오 ).

당신이 경우 절대 참조 평등 검사를 필요로하지 않는다 (또는 귀하의 비즈니스 도메인에 의해 쓸모 렌더링됩니다), 다음 주위 참조 평등 체크를 유지하는 이유가 없다. 그런 다음 개발자 오류방지 하기 위해 값 평등 검사로 바꾸는 것이 좋습니다 .
그러나 나중에 참조 평등 검사가 필요할 경우 다시 구현하려면 상당한 노력이 필요할 수 있습니다.


참조 유형이 콘텐츠 평등을 정의하지 않을 것이라고 기대하는 이유가 궁금합니다. 대부분의 유형은 참조 평등 을 하기 때문 아니라 도메인에 필수적이지 않기 때문에 평등을 정의하지 않습니다 .
카사 블랑카

3
@ casablanca : 나는 당신이 "예상"의 다른 정의를 해석하고 있다고 생각합니다 (즉 요구 사항과 가정). 문서가 없으면 ==기본 동작이므로 참조 평등 을 확인하는 것으로 가정 합니다. 그러나 ==실제로 값 평등을 검사하는 경우 명시 적으로 문서화 될 것으로 기대합니다. 기본적으로I'm curious why you expect that reference types won't define content equality. 정의 하지는 않지만 그렇다고 할 수는 없습니다. 나는 그것이 끝날 수 없다고 말하지 않았으며, 기본적으로 그것을 기대하지 않습니다 (예를 들어).
Flater

명확하게 해주셔서 감사합니다.
casablanca

2

기본적으로 문자열을 비교하므로 동일한 XML 내용을 포함하는 두 개의 래퍼가 동일하지 않은 것으로 간주되면 놀라지 않을 것입니다.

관용적 규칙은 일반적으로 참조 유형 객체에 적합 할 수 있지만 문자열은 관용적 의미에서 특별합니다. 기술적으로는 참조 유형이지만 값으로 취급하고 간주해야합니다.

래퍼 접미사는 혼란을 더합니다. 기본적으로 "XML 요소 아님"이라고합니다. 그래서 나는 그것을 참조 유형으로 취급해야합니까? 의미 적으로 이것은 의미가 없습니다. 클래스 이름을 XmlContent로 지정하면 혼란이 줄어 듭니다. 이는 기술적 구현 세부 사항이 아니라 컨텐츠에 관심이 있다는 신호입니다.


"문자열로 만들어진 객체"와 "기본적으로 문자열"사이의 한계를 어디에 두겠습니까? 외부 API 관점에서 상당히 모호한 정의 인 것 같습니다. API 사용자는 실제로 동작을 추측하기 위해 내부를 추측해야합니까?
Arthur Havlicek

클래스 / 객체의 @Arthur 시맨틱은 실마리를 제공해야합니다. 그리고 때로는 그다지 명백하지는 않으므로이 질문입니다. 실제 가치 유형의 경우 누구에게나 분명합니다. 실제 문자열도. 유형은 궁극적으로 비교가 내용 또는 객체 동일성을 포함해야하는지 여부를 알려야합니다.
마틴 마트
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.