Sonsuz özyineleme olmadan '==' operatör aşırı yüklemesinde boş değerleri nasıl kontrol ederim?


114

Aşağıdakiler == operatör aşırı yükleme yönteminde sonsuz özyinelemeye neden olacaktır

    Foo foo1 = null;
    Foo foo2 = new Foo();
    Assert.IsFalse(foo1 == foo2);

    public static bool operator ==(Foo foo1, Foo foo2) {
        if (foo1 == null) return foo2 == null;
        return foo1.Equals(foo2);
    }

Boş değerleri nasıl kontrol ederim?

Yanıtlar:


139

Kullanım 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);
}

Bu çözüm için geçerli değilAssert.IsFalse(foo2 == foo1);
FIL

Ve foo1.Equals(foo2)mesela, foo1 == foo2sadece eğer istiyorsam ne anlama geliyor foo1.x == foo2.x && foo1.y == foo2.y? Bu cevap, davayı görmezden gelmek foo1 != nulldeğil foo2 == nullmi ama ?
Daniel

Not: Daha basit sözdizimiyle aynı çözüm:if (foo1 is null) return foo2 is null;
Rem

20

Aşırı yükleme yönteminde nesneye çevirin:

public static bool operator ==(Foo foo1, Foo foo2) {
    if ((object) foo1 == null) return (object) foo2 == null;
    return foo1.Equals(foo2);
}

1
Kesinlikle. Hem (object)foo1 == nullveya foo1 == (object)nullgidecek yerleşik aşırı yük ==(object, object)değil, kullanıcı tanımlı aşırı yüklenmesine ==(Foo, Foo). Yöntemlerdeki aşırı yük çözümü gibidir.
Jeppe Stig Nielsen

2
Gelecekteki ziyaretçiler için - kabul edilen cevap, nesnenin == işlevini yürüten bir işlevdir. Bu temelde kabul edilen cevapla aynıdır ve bir dezavantajı vardır: Bir alçıya ihtiyacı vardır. Kabul edilen cevap bu nedenle üstündür.
Mafii

1
@Mafii Oyuncular tamamen bir derleme zamanı işlemidir. Derleyici, dökümün başarısız olamayacağını bildiğinden, çalışma zamanında hiçbir şeyi kontrol etmesi gerekmez. Yöntemler arasındaki farklar tamamen estetiktir.
17'de

8

Kullanın ReferenceEquals. Gönderen MSDN forumlarında :

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

4

Deneyin Object.ReferenceEquals(foo1, null)

Her neyse, ==operatörü aşırı yüklemenizi tavsiye etmem ; referansları karşılaştırmak ve Equals"anlamsal" karşılaştırmalar için kullanılmalıdır.


4

Geçersiz kıldıysam bool Equals(object obj)ve işleci ==ve Foo.Equals(object obj)aynı değeri döndürmek istiyorsam, genellikle !=işleci şu şekilde uygularım :

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

Operatör ==daha sonra benim için tüm boş kontrolleri yaptıktan sonra foo1.Equals(foo2), ikisinin eşit olup olmadığını gerçek kontrolü yapmak için geçersiz kıldığımı arayacak .


Bu çok uygun geliyor; uygulamasına Object.Equals(Object, Object)yan yana Object.ReferenceEquals(Object, Object)bakıldığında Object.Equals(Object, Object), her şeyi kutudan çıkan diğer yanıtlarda önerildiği gibi yaptığı oldukça açık . Neden kullanmıyorsun?
tne

@tne Çünkü ==tek istediğiniz varsayılan davranışsa operatörü aşırı yüklemenin bir anlamı yok . Yalnızca özel karşılaştırma mantığı uygulamanız gerektiğinde, yani bir referans eşitlik kontrolünden daha fazlası olduğunda aşırı yüklemelisiniz.
Dan Bechard

@Dan sözümü yanlış anladığına eminim; Aşırı ==yüklemenin arzu edilebilir olduğu halihazırda belirlenmiş bir bağlamda (soru bunu ima eder) basitçe bu yanıtı, Object.Equals(Object, Object)kullanmak ReferenceEqualsveya açık yayınlar gibi diğer hileleri gereksiz kıldığını (dolayısıyla "neden kullanmayalım?", "o") önererek destekliyorum. olmak Equals(Object, Object)). İlgisiz de olsa, görüşünüz de doğrudur ve daha da ileri gideceğim: sadece =="değer nesneleri" olarak sınıflandırabileceğimiz nesneler için aşırı yükleme .
tne

@tne temel fark olmasıdır Object.Equals(Object, Object)sırayla çağrıları Object.Equals (Object) sanal bir yöntem olduğunu Foo olasılıkla geçersiz kılar ise. Eşitlik kontrolünüze sanal bir çağrı eklemiş olmanız, derleyicinin bu çağrıları optimize etme (örn. Satır içi) yeteneğini etkileyebilir. Bu, çoğu amaç için muhtemelen ihmal edilebilir, ancak bazı durumlarda bir eşitlik operatöründeki küçük bir maliyet, döngüler veya sıralanmış veri yapıları için büyük bir maliyet anlamına gelebilir.
Dan Bechard

@tne Sanal yöntem çağrılarını optimize etmenin karmaşıklıkları hakkında daha fazla bilgi için, stackoverflow.com/questions/530799/… adresine bakın .
Dan Bechard

3

C # 7 veya sonraki bir sürümünü kullanıyorsanız, boş sabit kalıp eşleştirmesini kullanabilirsiniz:

public static bool operator==(Foo foo1, Foo foo2)
{
    if (foo1 is null)
        return foo2 is null;
    return foo1.Equals(foo2);
}

Bu size çağıran nesneden biraz daha düzgün bir kod verir .ReferenceEquals (foo1, null)


2
veyapublic static bool operator==( Foo foo1, Foo foo2 ) => foo1?.Equals( foo2 ) ?? foo2 is null;
Danko Durbić

3

Aslında nullbu durumda kontrol etmenin daha basit bir yolu var :

if (foo is null)

Bu kadar!

Bu özellik C # 7'de tanıtıldı


1

Benim yaklaşımım yapmak

(object)item == null

Bunun üzerine objectkendi eşitlik operatörüne güveniyorum ki bu yanlış gidemez. Veya özel bir uzantı yöntemi (ve bir aşırı yükleme):

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

veya daha fazla vakayı ele almak için şunlar olabilir:

public static bool IsNull<T>(this T obj) where T : class
{
    return (object)obj == null || obj == DBNull.Value;
}

Kısıtlama IsNull, değer türlerini engeller . Şimdi çağırmak kadar tatlı

object obj = new object();
Guid? guid = null; 
bool b = obj.IsNull(); // false
b = guid.IsNull(); // true
2.IsNull(); // error

Bu, baştan sona null değerleri kontrol etmek için tutarlı / hataya eğilimli olmayan bir tarzım olduğu anlamına geliyor. Ayrıca, (object)item == nulldaha hızlı olduğunuObject.ReferenceEquals(item, null) da buldum , ancak yalnızca önemliyse (şu anda her şeyi mikro optimize etmem gereken bir şey üzerinde çalışıyorum!).

Eşitlik kontrollerinin uygulanmasına ilişkin eksiksiz bir kılavuz görmek için bkz . Bir Referans Türünün İki Örneğini Karşılaştırmak İçin "En İyi Uygulama" Nedir?


Nitpick: Okuyucular, karşılaştırma DbNull, IMO gibi özelliklere atlamadan önce bağımlılıklarına dikkat etmelidir , bunun SRP ile ilgili sorunlar yaratmayacağı durumlar oldukça nadirdir. Sadece kod kokusuna dikkat çekmek çok uygun olabilir.
tne

0

Statik Equals(Object, Object)yöntem , iki nesnenin objAve objB, eşit olup olmadığını gösterir . Aynı zamanda değeri nulleşitlik için olan nesneleri test etmenizi sağlar . Bu karşılaştırır objAve objBaşağıdaki gibi eşitlik için:

  • İki nesnenin aynı nesne referansını temsil edip etmediğini belirler. Yaparlarsa, yöntem geri döner true. Bu test, ReferenceEqualsyöntemi çağırmakla eşdeğerdir . Buna ek olarak, her ikisi de eğer objAve objBvardır null, yöntem döner true.
  • Bu tespit eder ya objAya da objBbir null. Eğer öyleyse, geri döner false. İki nesne aynı nesne referansını temsil etmiyorsa ve ikisi de temsil etmiyorsa , sonucu nullçağırır objA.Equals(objB)ve döndürür. Bu objA, Object.Equals(Object)yöntemi geçersiz kılarsa, bu geçersiz kılmanın çağrıldığı anlamına gelir .

.

public static bool operator ==(Foo objA, Foo objB) {
    return Object.Equals(objA, objB);
}

0

Burada yinelenen olarak yeniden yönlendiren null ile nasıl karşılaştırılacağını geçersiz kılan operatöre daha fazla yanıt vermek .

Bunun Değer Nesnelerini desteklemek için yapıldığı durumlarda, yeni gösterimi kullanışlı buluyorum ve karşılaştırmanın yapıldığı tek bir yer olduğundan emin olmak istiyorum. Ayrıca Object.Equals (A, B) 'den yararlanmak, boş kontrolleri basitleştirir.

Bu ==,! =, Eşittir ve GetHashCode'u aşırı yükler

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

Daha karmaşık nesneler için Eşittir'de ek karşılaştırmalar ve daha zengin bir GetHashCode ekleyin.


0

Modern ve yoğun bir sözdizimi için:

public static bool operator ==(Foo x, Foo y)
{
    return x is null ? y is null : x.Equals(y);
}

public static bool operator !=(Foo x, Foo y)
{
    return x is null ? !(y is null) : !x.Equals(y);
}

-3

Operatörün aşırı sık görülen bir hata == kullanmaktır (a == b), (a ==null)ya da (b == null)bir referans eşitlik kontrol etmek için. Bunun yerine , aşırı yüklenmiş operatör == için bir çağrı ile sonuçlanır ve bir infinite loop. ReferenceEqualsDöngüyü önlemek için türü kullanın veya Object'e çevirin.

bunu kontrol et

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

Aşırı Yükleme Eşittir () ve Operatör için referans Yönergeleri ==


1
Tüm bu bilgilerle zaten birden fazla yanıt var. Aynı cevabın 7. kopyasına ihtiyacımız yok.
2016

-5

Bir nesne özelliğini kullanmayı deneyebilir ve sonuçta ortaya çıkan NullReferenceException'ı yakalayabilirsiniz. Denediğiniz özellik Object'ten miras alınmışsa veya geçersiz kılınmışsa, bu herhangi bir sınıf için işe yarar.

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

Çok sayıda boş nesneniz varsa, istisna işleme büyük bir yük olabilir.
Kasprzol

2
Haha, bunun en iyi yöntem olmadığına katılıyorum. Bu yöntemi gönderdikten sonra, mevcut projemi hemen bunun yerine ReferenceEquals'ı kullanacak şekilde revize ettim. Ancak, yetersiz olmasına rağmen işe yarıyor ve bu nedenle soruya geçerli bir cevap.
The Digital Gabeg
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.