답변:
사용 ReferenceEquals
:
Foo foo1 = null;
Foo foo2 = new Foo();
Assert.IsFalse(foo1 == foo2);
public static bool operator ==(Foo foo1, Foo foo2) {
if (object.ReferenceEquals(null, foo1))
return object.ReferenceEquals(null, foo2);
return foo1.Equals(foo2);
}
foo1.Equals(foo2)
예를 들어, 내가 원하는, 경우 수단을 foo1 == foo2
경우에만 foo1.x == foo2.x && foo1.y == foo2.y
? 이 대답이 어디 경우를 무시하고 foo1 != null
있지 foo2 == null
않습니까?
if (foo1 is null) return foo2 is null;
오버로드 메서드에서 객체로 캐스트 :
public static bool operator ==(Foo foo1, Foo foo2) {
if ((object) foo1 == null) return (object) foo2 == null;
return foo1.Equals(foo2);
}
(object)foo1 == null
또는 둘 다 사용자 정의 오버로드가 아닌 foo1 == (object)null
기본 제공 오버로드로 이동합니다 . 메서드에 대한 과부하 해결과 같습니다. ==(object, object)
==(Foo, Foo)
사용 ReferenceEquals
. 로부터 MSDN 포럼 :
public static bool operator ==(Foo foo1, Foo foo2) {
if (ReferenceEquals(foo1, null)) return ReferenceEquals(foo2, null);
if (ReferenceEquals(foo2, null)) return false;
return foo1.field1 == foo2.field2;
}
재정의 bool Equals(object obj)
하고 연산자를 원 ==
하고 Foo.Equals(object obj)
동일한 값을 반환하려면 일반적으로 다음 !=
과 같이 연산자를 구현합니다 .
public static bool operator ==(Foo foo1, Foo foo2) {
return object.Equals(foo1, foo2);
}
public static bool operator !=(Foo foo1, Foo foo2) {
return !object.Equals(foo1, foo2);
}
운영자 ==
는 나를 위해 모든 null 검사를 수행 한 후 foo1.Equals(foo2)
두 가지가 동일한 경우 실제 검사를 수행하도록 재정의했다고 호출 합니다.
Object.Equals(Object, Object)
나란히 살펴보면 다른 답변에서 제안한대로 모든 것을 즉시 수행하는 것이 Object.ReferenceEquals(Object, Object)
매우 분명합니다 Object.Equals(Object, Object)
. 왜 사용하지 않습니까?
==
원하는 모든 것이 기본 동작이면 연산자를 오버로딩하는 지점이 없기 때문 입니다. 사용자 지정 비교 논리를 구현해야하는 경우에만 오버로드해야합니다. 즉, 참조 동등성 검사 이상입니다.
==
이 바람직하다는 것이 이미 확립 된 맥락에서 (질문이 암시 함) 나는 단순히 Object.Equals(Object, Object)
사용 ReferenceEquals
또는 명시 적 캐스트 와 같은 다른 트릭을 불필요하게 만드는 것을 제안함으로써이 답변을 지원하고 있습니다 (따라서 "왜 사용하지 않습니까?", "그것"). 인 Equals(Object, Object)
). 관련이없는 경우에도 귀하의 요점이 정확하고 더 나아갈 것입니다. ==
객체에 대한 과부하 만 "가치 객체"로 분류 할 수 있습니다.
Object.Equals(Object, Object)
차례로 호출 한다는 것입니다. 동등성 검사에 가상 호출을 도입했다는 사실은 이러한 호출을 최적화 (예 : 인라인)하는 컴파일러의 기능에 영향을 미칠 수 있습니다. 이것은 대부분의 목적에서 무시할 수있을 것입니다. 그러나 어떤 경우에는 같음 연산자의 작은 비용이 루프 나 정렬 된 데이터 구조에 막대한 비용을 의미 할 수 있습니다.
C # 7 이상을 사용하는 경우 null 상수 패턴 일치를 사용할 수 있습니다.
public static bool operator==(Foo foo1, Foo foo2)
{
if (foo1 is null)
return foo2 is null;
return foo1.Equals(foo2);
}
이것은 하나의 호출 객체보다 약간 깔끔한 코드를 제공합니다.
public static bool operator==( Foo foo1, Foo foo2 ) => foo1?.Equals( foo2 ) ?? foo2 is null;
내 접근 방식은
(object)item == null
내가 object
잘못 갈 수없는 '자신의 평등 연산자 에 의존하고 있습니다. 또는 사용자 지정 확장 메서드 (및 오버로드) :
public static bool IsNull<T>(this T obj) where T : class
{
return (object)obj == null;
}
public static bool IsNull<T>(this T? obj) where T : struct
{
return !obj.HasValue;
}
또는 더 많은 케이스를 처리하려면 다음과 같습니다.
public static bool IsNull<T>(this T obj) where T : class
{
return (object)obj == null || obj == DBNull.Value;
}
제약 조건 IsNull
은 값 유형을 방지 합니다. 이제 부르는 것만 큼 달콤 해
object obj = new object();
Guid? guid = null;
bool b = obj.IsNull(); // false
b = guid.IsNull(); // true
2.IsNull(); // error
즉, 전체적으로 null을 검사하는 일관된 / 오류가 발생하지 않는 스타일이 하나 있습니다. 나는 또한. (object)item == null
보다 아주 약간 빠르다Object.ReferenceEquals(item, null)
는 것을 알았다 . 그러나 그것이 중요한 경우에만 (나는 현재 모든 것을 마이크로 최적화 해야하는 작업을하고있다!)
동등성 검사 구현에 대한 전체 가이드를 보려면 참조 유형의 두 인스턴스를 비교하는 "모범 사례"란 무엇입니까?를 참조하십시오.
정적 Equals(Object, Object)
메소드는 두 개체 여부를 나타내는, objA
와 objB
동일하다. 또한 값이 같은 개체를 테스트 할 수도 있습니다 null
. 그것은 비교 objA
하고 objB
다음과 같이 평등 :
true
. 이 테스트는 ReferenceEquals
메서드 호출과 동일합니다 . 또한, 두 경우 objA
와는 objB
이다 null
, 메소드가 리턴 true
.objA
또는 objB
이다 null
. 그렇다면 false
. 두 객체가 동일한 객체 참조를 나타내지 않고 둘 다 아닌 경우 null
, 호출 objA.Equals(objB)
하고 결과를 반환합니다. 즉 objA
, Object.Equals(Object)
메서드를 재정의하면 이 재정의가 호출됩니다..
public static bool operator ==(Foo objA, Foo objB) {
return Object.Equals(objA, objB);
}
여기에서 중복으로 리디렉션 되는 null과 비교하는 방법 을 재정의하는 연산자에 더 많이 응답 합니다.
Value Objects를 지원하기 위해 이것이 수행되는 경우 새로운 표기법이 편리하다는 것을 알게되었으며 비교가 이루어지는 곳이 한 곳만 있는지 확인하고 싶습니다. 또한 Object.Equals (A, B)를 활용하면 null 검사가 단순화됩니다.
이것은 ==,! =, Equals 및 GetHashCode를 오버로드합니다.
public static bool operator !=(ValueObject self, ValueObject other) => !Equals(self, other);
public static bool operator ==(ValueObject self, ValueObject other) => Equals(self, other);
public override bool Equals(object other) => Equals(other as ValueObject );
public bool Equals(ValueObject other) {
return !(other is null) &&
// Value comparisons
_value == other._value;
}
public override int GetHashCode() => _value.GetHashCode();
더 복잡한 개체의 경우 Equals 및 더 풍부한 GetHashCode에 추가 비교를 추가합니다.
연산자 == 오버로드의 일반적인 오류는
(a == b)
, 를 사용(a ==null)
하거나(b == null)
참조 동등성을 확인하는 것입니다. 대신 오버로드 된 연산자 == 에 대한 호출이 발생하여infinite loop
.ReferenceEquals
루프를 피하기 위해 유형을 사용 하거나 Object로 캐스트하십시오.
이것 좀 봐
// If both are null, or both are same instance, return true.
if (System.Object.ReferenceEquals(a, b))// using ReferenceEquals
{
return true;
}
// If one is null, but not both, return false.
if (((object)a == null) || ((object)b == null))// using casting the type to Object
{
return false;
}
Equals () 및 연산자 오버로드에 대한 참조 지침 ==
개체 속성을 사용하고 결과 NullReferenceException을 잡을 수 있습니다. 시도한 속성이 Object에서 상속되거나 재정의되면 모든 클래스에서 작동합니다.
public static bool operator ==(Foo foo1, Foo foo2)
{
// check if the left parameter is null
bool LeftNull = false;
try { Type temp = a_left.GetType(); }
catch { LeftNull = true; }
// check if the right parameter is null
bool RightNull = false;
try { Type temp = a_right.GetType(); }
catch { RightNull = true; }
// null checking results
if (LeftNull && RightNull) return true;
else if (LeftNull || RightNull) return false;
else return foo1.field1 == foo2.field2;
}
Assert.IsFalse(foo2 == foo1);