İşlevler null veya boş bir nesne döndürmeli mi?


209

Fonksiyonlardan veri döndürürken en iyi uygulama nedir ? Boş veya boş bir nesne döndürmek daha mı iyi? Ve neden biri diğerinin üzerinde yapsın?

Bunu düşün:

public UserEntity GetUserById(Guid userId)
{
     //Imagine some code here to access database.....

     //Check if data was returned and return a null if none found
     if (!DataExists)
        return null; 
        //Should I be doing this here instead? 
        //return new UserEntity();  
     else
        return existingUserEntity;
}

Bu programda, bu GUID ile veritabanında kullanıcı bilgisi olmayacak geçerli durumlar olacağını varsayalım. Bu durumda bir istisna atmanın uygun olmayacağını düşünürdüm ?? Ayrıca, istisna işlemenin performansa zarar verebileceği izlenimindeyim.


4
Bence demek istiyorsun if (!DataExists).
Sarah Vessels

107
Bu mimari bir sorudur ve mükemmel bir şekilde uygundur. OP'nin sorusu, çözmeye çalıştığı iş sorunundan bağımsız olarak geçerlidir.
Joseph Ferris

2
Bu soru zaten yeterince cevaplandı. Bence bu çok ilginç bir soru.
John Scipione

'getUser ()' null değerini döndürmelidir. 'getCurrentUserInfo ()' veya 'getCurrentPermissions ()', OTOH, daha açıklayıcı sorular olacaktır - kimin veya kimin oturum açmış olduğuna bakılmaksızın null olmayan bir yanıt nesnesi döndürmelidir .
Thomas W

2
Hayır @Bergi diğeri yineleniyor. Önce benimkine, Ekim ayında diğerine Aralık ayında 3 ay sonra soruldu. Ayrıca diğeri biraz farklı olan bir koleksiyondan bahsediyor.
7wp

Yanıtlar:


207

Hiçbir veri olmadığını belirtmek istiyorsanız null değerini döndürmek genellikle en iyi fikirdir.

Boş bir nesne verilerin döndürüldüğünü gösterirken, null değerini döndürmek hiçbir şeyin döndürülmediğini açıkça gösterir.

Buna ek olarak, nesnedeki üyelere erişmeye çalışırsanız, buggy kodunu vurgulamak için yararlı olabilecek null bir istisna ile sonuçlanır. Boş bir nesnenin üyelerine erişim başarısız olmaz, bu da hataların bulunamayacağı anlamına gelir.


21
Bir istisna atmalı, problemi yutmamalı ve null döndürmemelisiniz. En azından bunu günlüğe kaydetmeli ve sonra devam etmelisiniz.
Chris Ballance

130
@Chris: Kabul etmiyorum. Kod, dönüş değerinin null olduğunu açıkça belgeliyorsa, ölçütlerinize uygun sonuç bulunamazsa null değerini döndürmek tamamen kabul edilebilir. Bir istisna fırlatmak İLK değil, SON seçiminiz olmalıdır.
Mike Hofer

12
@Chris: Buna hangi temelde karar veriyorsunuz? Denkleme günlük kaydı eklemek kesinlikle aşırı görünüyor. Tüketici kodunun, herhangi bir kullanıcı yoksa ne yapılması gerektiğine karar vermesine izin verin. Önceki yorumumda olduğu gibi, evrensel olarak "veri yok" olarak tanımlanan bir değeri döndürmeyle ilgili kesinlikle bir sorun yok.
Adam Robinson

17
Bir Microsoft geliştiricisinin "dönen boş" ın "sorunu yutmaya" eşdeğer olduğuna inandığı için biraz şaşırdım. Bellek hizmet veriyorsa, Çerçeve'de arayanın isteğiyle eşleşen hiçbir şey yoksa null yöntemlerinin döndürüldüğü tonlarca yöntem vardır. Bu "sorunu yutuyor mu?"
Mike Hofer

5
Son fakat en az değil bool GetUserById(Guid userId, out UserEntity result), ben olurdu - hangi "null" dönüş-değeri tercih ediyorum, ve hangi bir istisna atma kadar aşırı değil. Güzel null-ücretsiz kod gibi sağlar if(GetUserById(x,u)) { ... }.
Marcel Jackwerth

44

Durumunuz için en anlamlı olan şeye bağlıdır.

Null değerini döndürmek mantıklı mı, örneğin "böyle bir kullanıcı yok" mu?

Yoksa varsayılan bir kullanıcı oluşturmak mantıklı mı? Bu, eğer bir kullanıcı YOKSA, yoksa, arama kodunun bir kullanıcı istediğinde var olmasını istediğini güvenli bir şekilde varsayabildiğinizde en mantıklıdır.

Yoksa arama kodu geçersiz bir kimliğe sahip bir kullanıcı talep ediyorsa bir istisna (la "FileNotFound") atmak mantıklı mı?

Bununla birlikte - endişelerin ayrılması / SRP açısından, ilk ikisi daha doğrudur. Ve teknik olarak ilki en doğru olanıdır (ancak sadece bir saç tarafından) - GetUserById sadece bir şeyden sorumlu olmalıdır - kullanıcıyı almak. Başka bir şey döndürerek kendi "kullanıcı yok" davasını ele almak SRP'yi ihlal edebilir. Farklı bir kontrole ayırmak - bool DoesUserExist(id)bir istisna atmayı seçerseniz uygun olur.

Aşağıdaki kapsamlı yorumlara dayanarak : Bu, API düzeyinde bir tasarım sorusuysa, bu yöntem "OpenFile" veya "ReadEntireFile" ile benzer olabilir. Bir kullanıcıyı bazı depolardan "açıyoruz" ve nesneyi sonuçtaki verilerden nemlendiriyoruz. Bu durumda bir istisna uygun olabilir . Öyle olmayabilir, ama olabilir.

Tüm yaklaşımlar kabul edilebilir - sadece API / uygulamanın daha geniş bağlamına bağlı.


Birisi sana oy verdi ve ben sana geri oy verdim, çünkü bu bana kötü bir cevap gibi görünmüyor; hariç: Posterin verdiği gibi bir yöntemde hiç kullanıcı bulamazken bir istisna atmam. Hiçbir kullanıcı geçersiz bir kimlik veya böyle bir istisnaya değer bir sorun anlamına gelmezse, bu daha yüksek olmalıdır - atma yönteminin bu kimliğin nereden geldiği vb. Hakkında daha fazla bilgi sahibi olması gerekir.
Jacob Mattison

(Sanırım aşağı oy, böyle bir durumda bir istisna atma fikrine bir itirazdı.)
Jacob Mattison

1
Son noktaya kadar kabul etti. Evrensel olarak "veri yok" olarak tanımlanan bir değer döndürerek SRP'nin ihlali söz konusu değildir. Bu, nerede yan tümcesi hiçbir sonuç üretmezse SQL veritabanının hata döndürmesi gerektiğini belirtmek gibidir. Bir İstisna geçerli bir tasarım seçimi olsa da (beni tüketici olarak rahatsız edecek bir seçenek olsa da), null değerini döndürmekten "daha doğru" değildir. Ve hayır, DV değilim.
Adam Robinson

@JacobM Varolmayan, null döndürmeyen, ancak veritabanlarından olmayan bir dosya sistemi yolu istediğimizde istisnalar atıyoruz. Bu yüzden açıkça her ikisi de uygundur, bu da elde ettiğim şey - sadece bağlıdır.
Rex M

2
@Charles: "Bir noktada bir istisna atılmalıysa" sorusunu cevaplıyorsunuz, ancak "bu işlev istisnayı attığında" sorusu cevaplıyorsunuz. Doğru cevap "belki", "evet" değil.
Adam Robinson

30

Şahsen NULL kullanıyorum. Geri döndürülecek veri olmadığı anlaşılıyor. Ancak bir Boş Nesnenin yararlı olabileceği durumlar vardır .


Bunu kendim cevap olarak eklemek üzereyim. NullObjectPattern veya özel durum şablonu. Sonra her vaka için bir tane uygulayabilirsiniz, NoUserEntitiesFound, NullUserEntities vb.
David Swindells

27

Dönüş türünüz bir dizi ise boş bir dizi döndürür, aksi takdirde null döndürür.


Bir listedeki 0 öğe şu anda atanmamış listeyle aynı mı?
AnthonyWJones

3
Listedeki 0 öğe ile aynı değil null. foreachEndişelenmeden ifadelerde ve linq sorgularında kullanmanıza izin verir NullReferenceException.
Darin Dimitrov

5
Bunun daha fazla onaylanmamasına şaşırdım. Bu benim için oldukça makul bir rehber gibi görünüyor.
shashi

Boş kap null nesne modelinin sadece belirli bir örneğidir. Hangisi uygun olabilir, söyleyemeyiz.
Deduplicator

Veri mevcut olmadığında boş bir dizi döndürmek yanlıştır . Kullanılabilir verilerle hiç öğe içermeyen veriler ile kullanılabilir olmayan veriler arasında bir fark vardır. Her iki durumda da boş bir dizi döndürmek, durumun hangisi olduğunu bilmeyi imkansız hale getirir. Sadece veri bile var olup olmadığını kontrol etmeden bir foreach kullanabilirsiniz bunu yapmak aptalca ötesinde - arayan veri var olup olmadığını kontrol etmek gerekir ve arayan bir hata ortaya çıkarır çünkü iyi değil bir NullReferenceException ..
Monica

12

Belirli bir sözleşme bozulursa (sadece) bir istisna atmalısınız.
Belirli bir örneğinizde, bilinen bir kimliğe dayalı bir UserEntity istemek, eksik (silinmiş) kullanıcıların beklenen bir durum olması gerçeğine bağlı olacaktır. Öyleyse, geri dönün, nullancak beklenen bir durum değilse, bir istisna atın.
İşlev çağrıldıysa UserEntity GetUserByName(string name), muhtemelen atmayacak, ancak null değerini döndüreceğini unutmayın. Her iki durumda da boş bir UserEntity döndürmek yararsız olacaktır.

Dizgiler, diziler ve koleksiyonlar için durum genellikle farklıdır. Yöntemlerin null'boş' bir liste olarak kabul etmesi gereken ancak MS yerine sıfır uzunluklu koleksiyonları döndürmesi gereken bazı MS formlarını hatırlıyorum null. Dizeler için de aynı şey geçerli. Boş diziler bildirebileceğinizi unutmayın:int[] arr = new int[0];


Dizelerin farklı olduğundan bahsettiğinize sevindim, çünkü Google boş bir dize döndürüp döndürmeyeceğime karar verirken bunu gösterdi.
Noumenon

Dizeler, koleksiyonlar ve diziler farklı değildir . MS öyle diyorsa MS yanlıştır. Boş bir dize ile null arasında ve boş bir toplama ile null arasında bir fark vardır. Her iki durumda da birincisi mevcut verileri (0 büyüklüğünde) ve ikincisi veri eksikliğini temsil eder. Bazı durumlarda ayrım çok önemlidir. Örneğin, bir önbellekteki bir girdiyi ararsanız, önbelleğe alınan ancak boş olan veriler ile önbelleğe alınmayan veriler arasındaki farkı bilmek istersiniz, böylece bu veriyi, temel veri kaynağından almanız gerekir. boş.
Monica

1
Konuyu ve bağlamı özlüyorsunuz. .Wher(p => p.Lastname == "qwerty")boş bir koleksiyon döndürmeli, değil null.
Henk Holterman

@HenkHolterman Koleksiyonun tamamına erişebiliyor ve koleksiyonda hiçbir öğe kabul etmeyen bir filtre uygulayabiliyorsanız, boş bir koleksiyon doğru sonuçtur. Ancak tam koleksiyon yoksa, boş bir koleksiyon son derece yanıltıcıdır - durumun normal veya istisnai olmasına bağlı olarak boş veya atma doğru olur. Gönderiniz hangi durumdan bahsettiğinizi nitelendirmediği için (ve şimdi eski durumdan bahsettiğinizi açıklığa kavuşturuyorsunuz) ve OP ikinci durumdan bahsederken, size katılmıyorum.
Monica

11

Bu, belirli bir Guid ID'ye sahip bir kullanıcının varlığının bu işlev için beklenen bir normal kullanım durumu olup olmadığına bağlı olarak veya bu yöntemin kullanıcıya sağladığı herhangi bir işlevi başarıyla tamamlamasını engelleyecek bir anormallik olup olmadığına bağlı bir iş sorusudur. itiraz etmek...

Bu bir "istisna" ise, bu kimliğe sahip bir kullanıcının bulunmaması, uygulamanın yaptığı her işlevi başarıyla tamamlamasını engelleyecektir, (Diyelim ki ürün gönderdiğimiz bir müşteri için fatura oluşturuyoruz ... ), bu durum bir ArgumentException (veya başka bir özel istisna) atmalıdır.

Eksik bir kullanıcı uygunsa, (bu işlevi çağırmanın potansiyel normal sonuçlarından biri), sonra bir boş döndürün ....

DÜZENLEME: (Adem'den başka bir cevaptaki yorumu ele almak için)

Uygulama birden fazla iş süreci içeriyorsa, bunlardan biri veya daha fazlası başarıyla tamamlanması için bir Kullanıcı gerektirir ve bunlardan biri veya daha fazlası kullanıcı olmadan başarıyla tamamlanabilirse, istisna, çağrı yığınına, daha yakın bir yere atılmalıdır. Kullanıcı gerektiren iş süreçleri bu yürütme konusunu çağırmaktadır. Bu yöntem ile o nokta arasındaki (istisnanın atıldığı yer) arasındaki yöntemler, hiçbir kullanıcının var olmadığını (boş, boole, her neyse - bu bir uygulama detayıdır) bildirmelidir.

Ancak uygulamadaki tüm işlemler bir kullanıcı gerektiriyorsa , yine de bu yöntemde istisna atıyorum ...


-1 aşağı doğru vekile, +1 Charles'a - bu tamamen bir iş sorusudur ve bunun için en iyi uygulama yoktur.
Austin Salonen

Akarsuları geçiyor. Bunun bir "hata koşulu" olup olmadığı iş mantığı tarafından yönlendirilir. Bunun nasıl ele alınacağı bir uygulama mimarisi kararıdır. İş mantığı, bir boş değerin geri döndürülmesini gerektirmez, sadece gereksinimlerin karşılandığını gösterir. İşletme yöntem getirisi türlerine karar veriyorsa, uygulamanın teknik yönüne çok fazla dahil olurlar.
Joseph Ferris

@joseph, "yapılandırılmış istisna işleme" nin temel prensibi, yöntemler uygulamak için kodlandıkları herhangi bir işlevi tamamlayamadığında istisnaların atılması gerektiğidir. Haklısınız, bu yöntemin uygulanması için kodlanan iş işlevi "başarıyla tamamlanabilir", (Etki Alanı Modelinde ne anlama gelirse), bir istisna atmanız gerekmediği için, boş bir değer döndürebilirsiniz. , veya bir boolean "FoundUser" değişkeni ya da her neyse ... Hiçbir kullanıcının bulunmadığı çağrı yöntemiyle nasıl iletişim kurduğunuz o zaman teknik bir uygulama ayrıntısı haline gelir.
Charles Bretana

10

Şahsen null dönecekti, çünkü DAL / Repository katmanının bu şekilde hareket etmesini beklerdim.

Eğer yoksa, bir nesneyi başarılı bir şekilde getirdiği düşünülebilecek bir şey döndürmeyin, null burada güzel çalışır.

En önemli şey, DAL / Repos Katmanınızda tutarlı olmaktır, bu şekilde nasıl kullanılacağı konusunda kafanız karışmaz.


7

niyetlendim

  • return nullnesne kimliği önceden bilinmediği zaman mevcut değilse , mevcuttur.
  • thrownesne kimliği yaparsa o zaman var olmayan gerektiğini mevcuttur.

Bu iki senaryoyu bu üç yöntemle ayırt ediyorum. İlk:

Boolean TryGetSomeObjectById(Int32 id, out SomeObject o)
{
    if (InternalIdExists(id))
    {
        o = InternalGetSomeObject(id);

        return true;
    }
    else
    {
        return false;
    }
}

İkinci:

SomeObject FindSomeObjectById(Int32 id)
{
    SomeObject o;

    return TryGetObjectById(id, out o) ? o : null;
}

Üçüncü:

SomeObject GetSomeObjectById(Int32 id)
{
    SomeObject o;

    if (!TryGetObjectById(id, out o))
    {
        throw new SomeAppropriateException();
    }

    return o;
}

Yani outdeğilref
Matt Ellen

@Matt: Evet, efendim, kesinlikle yaparım! Sabit.
Johann Gerell

2
Gerçekten, bu tek bedene uyan tek cevap ve bu yüzden tamamen ve sadece gerçek! :) Evet, yöntemin çağrıldığı varsayımlara bağlıdır ... Önce bu varsayımları temizleyin ve sonra yukarıdakilerin doğru kombinasyonunu seçin. Buraya ulaşmak için çok fazla aşağı kaydırmak zorunda kaldım :) +100
yair

Bu, async yöntemlerini desteklemediği için kullanılacak desen gibi görünüyor. Bu cevaba referans verdim ve Tuple
Literals ile asenkron

6

Yine başka bir yaklaşım, değer üzerinde çalışacak bir geri çağırma nesnesine veya delege aktarmayı içerir. Bir değer bulunamazsa geri arama yapılmaz.

public void GetUserById(Guid id, UserCallback callback)
{
    // Lookup user
    if (userFound)
        callback(userEntity);  // or callback.Call(userEntity);
}

Kodunuzun her yerinde boş denetimlerden kaçınmak istediğinizde ve bir değer bulamadığınızda hata olmadığında bu iyi çalışır. Herhangi bir özel işleme ihtiyaç duyduğunuzda hiçbir nesne bulunmadığında da bir geri arama sağlayabilirsiniz.

public void GetUserById(Guid id, UserCallback callback, NotFoundCallback notFound)
{
    // Lookup user
    if (userFound)
        callback(userEntity);  // or callback.Call(userEntity);
    else
        notFound(); // or notFound.Call();
}

Tek bir nesne kullanan aynı yaklaşım şöyle görünebilir:

public void GetUserById(Guid id, UserCallback callback)
{
    // Lookup user
    if (userFound)
        callback.Found(userEntity);
    else
        callback.NotFound();
}

Tasarım açısından, bu yaklaşımı gerçekten seviyorum, ancak birinci sınıf işlevleri kolayca desteklemeyen dillerde çağrı sitesi bulkier yapmanın dezavantajı var.


İlginç. Delegeler hakkında konuşmaya başladığınızda, hemen Lambda ifadelerinin burada kullanılıp kullanılamayacağını merak etmeye başladım.
7wp

Evet! Anladığım kadarıyla, C # 3.0 ve up'un lambda sözdizimi temelde anonim delegeler için sözdizimsel şekerdir. Benzer şekilde, Java'da, güzel lambda veya anonim delege sözdizimi olmadan, anonim bir sınıf oluşturabilirsiniz. Biraz daha çirkin, ama gerçekten kullanışlı olabilir. Bugünlerde C # örneğim Func <UserEntity> veya adlandırılmış bir temsilci yerine bunun gibi bir şey kullanmış olabilirdi, ancak üzerinde çalıştığım son C # projesi hala 2. sürümü kullanıyordu.
Marc

+1 Bu yaklaşımı seviyorum. Ancak bir sorun, geleneksel olmaması ve kod temeli giriş engelini hafifçe artırmasıdır.
timoxley

4

CSLA.NET kullanıyoruz ve başarısız bir veri getirme işleminin "boş" bir nesne döndürmesi gerektiği görüşünde. Bu aslında oldukça can sıkıcı, çünkü obj.IsNewrathern'inobj == null .

Bir önceki afişin de belirttiği gibi, boş dönüş değerleri, kodun hemen başarısız olmasına neden olarak boş nesnelerin neden olduğu gizlilik sorunlarının olasılığını azaltır.

Bence null daha zarif .

Bu çok yaygın bir durumdur ve buradaki insanların buna şaşırdıklarıma şaşırdım: herhangi bir web uygulamasında, veriler genellikle bir sorgu dizisi parametresi kullanılarak getirilir, bu da açıkça karıştırılabilir, bu nedenle geliştirici " ".

Bunu şu şekilde halledebilirsiniz:

if (User.Exists (id)) {
  this.User = User.Fetch (id);
} Başka {
  Response.Redirect ( "~ / notfound.aspx");
}

... ancak bu, her seferinde veritabanına ekstra bir çağrıdır, bu da yüksek trafikli sayfalarda bir sorun olabilir. Buna karşılık:

this.User = User.Fetch (id);

if (this.User == null) {
  Response.Redirect ( "~ / notfound.aspx");
}

... sadece bir telefon görüşmesi gerektirir.


4

Ben nullnull birleştirme operatörü ( ??) ile uyumlu olduğundan tercih ederim .


4

Boş bir nesne yerine null dönüş diyebilirim.

Ancak burada bahsettiğiniz özel örnek, bir kullanıcı için kullanıcı kimliğine göre arama yapıyorsunuz, bu kullanıcının anahtarı gibi, bu durumda muhtemelen hiçbir kullanıcı örneği örneği bulunmazsa bir istisna atmak isterim .

Genelde takip ettiğim kural budur:

  • Birincil anahtar işlemiyle bulmada hiçbir sonuç bulunmazsa, ObjectNotFoundException öğesini atın.
  • Diğer ölçütlere göre bir bulguda sonuç bulunamazsa null değerini döndürün.
  • Birden fazla nesneyi döndürebilecek anahtar olmayan ölçütlere göre bir sonuçta hiçbir sonuç bulunamazsa, boş bir koleksiyon döndürür.

Neden bu durumların herhangi birinde istisna atıyorsunuz? Bazen kullanıcılar veritabanında bulunmazlar ve bunun gerçekleşmeyeceğini umarız. İstisnai bir davranış değil.
siride

3

Bağlama göre değişecektir, ancak belirli bir nesneyi (örneğin örneğinde olduğu gibi) ararken genellikle boş değer döndürür ve bir nesne kümesi arıyorum ancak boş bir koleksiyon varsa boş bir koleksiyon döndürür.

Kodunuzda bir hata yaptıysanız ve null döndürmek boş gösterici istisnalarına yol açarsa, bunu ne kadar erken yakalarsanız o kadar iyi olur. Boş bir nesneyi döndürürseniz, nesnenin ilk kullanımı işe yarayabilir, ancak daha sonra hata alabilirsiniz.


+1 Burada söylediğinizle aynı mantığı
soruyordum

3

Bu durumda en iyisi böyle bir kullanıcı yok bir durumda "null" dönüş. Ayrıca yönteminizi statik hale getirin.

Düzenle:

Genellikle böyle yöntemler bazı "Kullanıcı" sınıfının üyeleridir ve örnek üyelerine erişimi yoktur. Bu durumda yöntem statik olmalıdır, aksi takdirde bir "Kullanıcı" örneği oluşturmalı ve sonra başka bir "Kullanıcı" örneği döndürecek GetUserById yöntemini çağırmalısınız. Katılıyorum bu kafa karıştırıcı. Ancak GetUserById yöntemi bazı "DatabaseFactory" sınıfının üyesiyse, bunu örnek üyesi olarak bırakmak sorun olmaz.


Yöntemimi neden statik hale getirmek istediğimi sorabilir miyim? Bağımlılık Enjeksiyonunu kullanmak istersem ne olur?
7wp

Tamam şimdi mantığını aldım. Ancak Havuz desenine sadık kalıyorum ve depolarım için bağımlılık enjeksiyonu kullanmayı seviyorum, bu nedenle statik yöntemler kullanamıyorum. Ama +1 null döndürmeyi önerdiği için :)
7wp

3

Şahsen nesnenin varsayılan bir örneğini döndürürüm. Bunun nedeni, yöntemin sıfırdan çoğuna veya sıfıra bire (yöntemin amacına bağlı olarak) dönmesini beklememdir. Bu yaklaşımı kullanarak herhangi bir tür hata durumu olmasının tek nedeni, yöntemin hiçbir nesne (ler) döndürmemesi ve her zaman beklenen (bire çok veya tekil dönüş açısından) olmasıdır.

Bunun bir iş alanı sorusu olduğu varsayımına gelince, bunu denklemin o tarafından görmüyorum. Dönüş türlerinin normalleştirilmesi geçerli bir uygulama mimarisi sorusudur. En azından, kodlama uygulamalarında standardizasyona tabidir. "Senaryo X, sadece boş verin" diyecek bir işletme kullanıcısı olduğundan şüpheliyim.


+1 Sorunun alternatif görüşünü seviyorum. Temel olarak, yöntem, uygulama boyunca tutarlı olduğu sürece hangi yaklaşımı seçeceğim yaklaşımın iyi olması gerektiğini söylüyorsunuz?
7wp

1
Bu benim inancım. Bence tutarlılık son derece önemli. İşleri birden çok yerde birden çok şekilde yaparsanız, yeni hatalar için daha yüksek bir risk oluşturur. Kişisel olarak varsayılan nesne yaklaşımına gittik, çünkü etki alanı modelimizde kullandığımız Öz kalıbıyla iyi çalışıyor. Doldurulup doldurulmadığını bize bildirmek için tüm etki alanı nesnelerine karşı test edebileceğimiz tek bir genel uzantı yöntemimiz var, böylece herhangi bir DO'nun bir objectname.IsDefault () çağrısıyla doğrudan test edilebileceğini biliyoruz. .
Joseph Ferris

3

İş Nesnelerimizde 2 ana Get yöntemimiz vardır:

Bağlamda işleri basit tutmak için veya soru sorduğunuzda:

// Returns null if user does not exist
public UserEntity GetUserById(Guid userId)
{
}

// Returns a New User if user does not exist
public UserEntity GetNewOrExistingUserById(Guid userId)
{
}

İlk yöntem belirli varlıkları alırken, ikinci yöntem özellikle web sayfalarına varlık eklerken veya düzenlerken kullanılır.

Bu, kullanıldıkları bağlamda her iki dünyanın en iyisini elde etmemizi sağlar.


3

Ben bir Fransız BT öğrencisiyim, bu yüzden fakir İngilizcemi affedin. Sınıflarımızda böyle bir yöntemin asla null veya boş bir nesne döndürmemesi gerektiği söyleniyor. Bu yöntemin kullanıcısının, aramaya çalışmadan önce aradığı nesnenin var olup olmadığını kontrol etmesi gerekir.

Java'yı kullanarak assert exists(object) : "You shouldn't try to access an object that doesn't exist";, "önkoşulu" ifade etmek için null döndürülebilecek herhangi bir yöntemin başında bir ekleme yapmanız istenir (ingilizce kelimenin ne olduğunu bilmiyorum).

IMO'nun kullanımı gerçekten kolay değil ama ben bunu kullanıyorum, daha iyi bir şey bekliyorum.


1
Cevabınız için teşekkürler. Ama önce var olup olmadığını kontrol etme fikrinden hoşlanmıyorum. Bunun nedeni, veritabanına ek bir sorgu oluşturmasıdır. Günde milyonlarca kişi tarafından erişilen bir uygulamada bu, dramatik performans kaybına neden olabilir.
7wp

1
Bunun bir yararı, varoluş kontrolünün uygun şekilde soyut olmasıdır: (userExists) biraz daha okunabilir, sorun alanına daha yakınsa ve daha az 'bilgisayar' ise: if (kullanıcı == null)
timoxley

Ve eğer '(x == null)', onlarca yıllık bir kalıpsa, daha önce görmediyseniz, çok uzun bir süre kod yazmadınız (ve olduğu gibi alışmalısınız) milyonlarca kod satırı). "Computery"? Veritabanı erişiminden bahsediyoruz ...
Lloyd Sargent

3

Kullanıcının bulunamaması durumu yeterince sık ortaya çıkıyorsa ve duruma bağlı olarak (bazen bir istisna atma, bazen boş bir kullanıcının yerine geçme) çeşitli şekillerde uğraşmak istiyorsanız, F # Optionveya Haskell'in Maybetipine yakın bir şey de kullanabilirsiniz. 'değersiz' vakasını 'bir şey buldu!' ifadesinden açıkça ayıran. Veritabanı erişim kodu şöyle görünebilir:

public Option<UserEntity> GetUserById(Guid userId)
{
 //Imagine some code here to access database.....

 //Check if data was returned and return a null if none found
 if (!DataExists)
    return Option<UserEntity>.Nothing; 
 else
    return Option.Just(existingUserEntity);
}

Ve şu şekilde kullanılabilir:

Option<UserEntity> result = GetUserById(...);
if (result.IsNothing()) {
    // deal with it
} else {
    UserEntity value = result.GetValue();
}

Ne yazık ki, herkes böyle bir tür yuvarlanıyor gibi görünüyor.


2

Genellikle null döndürür. İstisnalar atmadan ve her yerde tonlarca deneme / yakalama kullanmadan bir şeyin berbat olup olmadığını tespit etmek için hızlı ve kolay bir mekanizma sağlar.


2

Koleksiyon türleri için bir Boş Koleksiyon döndürürdüm, diğer tüm türler için NullObject kalıplarını döndüren türle aynı arabirimi uygulayan bir nesneyi döndürmek için kullanmayı tercih ederim. kalıp teslim bağlantı metni hakkında ayrıntılar için

NullObject desenini kullanarak bu şöyle olur: -

public UserEntity GetUserById(Guid userId)

{// Veritabanına erişmek için burada bir kod düşünün .....

 //Check if data was returned and return a null if none found
 if (!DataExists)
    return new NullUserEntity(); //Should I be doing this here instead? return new UserEntity();  
 else
    return existingUserEntity;

}

class NullUserEntity: IUserEntity { public string getFirstName(){ return ""; } ...} 

2

Başkalarının söylediklerini daha titizlikle ifade etmek için ...

İstisnalar İstisnai durumlar içindir

Bu yöntem saf veri erişim katmanı ise, bir select deyiminde yer alan bazı parametre göz önüne alındığında, bir nesne oluşturmak için herhangi bir satır bulamayabilir ve bu nedenle null döndüren kabul edilebilir olacağını söyleyebilirim veri erişim mantığıdır.

Öte yandan, parametremin birincil anahtarı yansıtmasını beklersem ve yalnızca bir satır geri almalıyım , birden fazla geri alırsam bir istisna atarım. 0 null değerini döndürmek için uygun, 2 değil.

Şimdi, bir LDAP sağlayıcısına karşı kontrol edilen bir giriş kodum varsa ve daha fazla bilgi almak için bir DB'ye karşı kontrol ettim ve bunların her zaman senkronize olması bekleniyordu, o zaman istisnayı fırlatabilirim. Diğerlerinin söylediği gibi, iş kuralları.

Şimdi bunun genel bir kural olduğunu söyleyeceğim . Bunu kırmak isteyebileceğiniz zamanlar vardır. Ancak, C # ( birçoğu ) ve Java (bunun bir kısmı) ile deneyimlerim ve deneylerim , istisnalarla başa çıkmanın akıllıca koşullu mantık yoluyla ele alınmaktan çok daha pahalı bir performans olduğunu öğretti . Bazı durumlarda 2 veya 3 büyüklükte daha pahalı ayarla konuşuyorum. Yani, eğer kodunuz bir döngüye girebilirse, null döndürmeyi ve test etmeyi öneririm.


2

Sahte php / kodumu affet.

Gerçekten sonucun kullanım amacına bağlı olduğunu düşünüyorum.

Dönüş değerini düzenlemek / değiştirmek ve kaydetmek istiyorsanız, boş bir nesne döndürün. Bu şekilde, yeni veya mevcut bir nesnedeki verileri doldurmak için aynı işlevi kullanabilirsiniz.

Birincil anahtar ve bir veri dizisi alır, satırı veri ile doldurur, sonra ortaya çıkan kaydı db kaydeder bir işlevi var diyelim. Nesneyi her iki şekilde de verilerimle doldurmayı planladığım için, boş bir nesneyi alıcıdan geri almak büyük bir avantaj olabilir. Bu şekilde her iki durumda da aynı işlemleri yapabilirim. Ne olursa olsun alıcı işlevinin sonucunu kullanırsınız.

Misal:

function saveTheRow($prim_key, $data) {
    $row = getRowByPrimKey($prim_key);

    // Populate the data here

    $row->save();
}

Burada aynı işlem serisinin bu türdeki tüm kayıtları manipüle ettiğini görebiliriz.

Ancak, dönüş değerinin nihai amacı veri okumak ve bir şey yapmak ise, o zaman null dönecekti. Bu şekilde, hiçbir veri döndürülmediğini çok hızlı bir şekilde belirleyebilir ve kullanıcıya uygun mesajı görüntüleyebilirim.

Genellikle, veri alır (böylece hata iletileri, vb ... günlüğe kaydedebilirsiniz) benim fonksiyonumda istisnalar yakalamak sonra doğrudan yakalama null döndürür. Genellikle son kullanıcı için sorunun ne olduğu önemli değildir, bu yüzden hata günlüğü / işlemimi doğrudan verileri alan işlevde kapsüllemek en iyisidir. Herhangi bir büyük şirkette paylaşılan bir kod tabanı sürdürüyorsanız, bu özellikle yararlıdır, çünkü en tembel programcıda bile uygun hata günlüğü tutma / işleme zorlayabilirsiniz.

Misal:

function displayData($row_id) {
    // Logging of the error would happen in this function
    $row = getRow($row_id);
    if($row === null) {
        // Handle the error here
    }

    // Do stuff here with data
}

function getRow($row_id) {
 $row = null;
 try{
     if(!$db->connected()) {
   throw excpetion("Couldn't Connect");
  }

  $result = $db->query($some_query_using_row_id);

  if(count($result) == 0 ) {
   throw new exception("Couldn't find a record!");
  }

  $row = $db->nextRow();

 } catch (db_exception) {
  //Log db conn error, alert admin, etc...
  return null; // This way I know that null means an error occurred
 }
 return $row;
}

Bu benim genel kuralım. Şimdiye kadar iyi çalıştı.


2

İlginç bir soru ve her zaman kodunuzun sorumluluğuna bağlı olduğu için "doğru" cevap olmadığını düşünüyorum. Metodunuz, bulunan verilerin sorun olup olmadığını biliyor mu? Çoğu durumda cevap "hayır" dır ve bu yüzden null değerini döndürmek ve arayan kişinin durumuyla ilgilenmesine izin vermek mükemmeldir.

Belki de fırlatma yöntemlerini boş dönen yöntemlerden ayırmak için iyi bir yaklaşım, ekibinizde bir kural bulmaktır: Bir şey "aldıklarını" söyleyen yöntemler, elde edilecek hiçbir şey yoksa bir istisna atmalıdır. Boş döndürülebilecek yöntemler farklı şekilde adlandırılabilir, bunun yerine "Bul ..." olabilir.


+1 Programcının bu işlevin nasıl kullanılacağını bildirmek için tek tip bir adlandırma kuralı kullanma fikrini seviyorum.
7wp

1
Aniden LINQ'nun yaptığı şeyin farkındayım: First (...) vs. FirstOrDefault (...)
Marc Wittke

2

Döndürülen nesne yinelenebilecek bir şeyse, boş bir nesne döndürürdüm, böylece önce null için test yapmak zorunda kalmam.

Misal:

bool IsAdministrator(User user)
{
    var groupsOfUser = GetGroupsOfUser(user);

    // This foreach would cause a run time exception if groupsOfUser is null.
    foreach (var groupOfUser in groupsOfUser) 
    {
        if (groupOfUser.Name == "Administrators")
        {
            return true;
        }
    }

    return false;
}

2

Herhangi bir yöntemden null döndürmek değil, bunun yerine Option işlev türünü kullanmak istiyorum. Sonuç döndüremeyen yöntemler null yerine boş bir Option döndürür.

Ayrıca, sonuç veremeyen bu tür yöntemler, isimleri yoluyla bunu belirtmelidir. Normalde boş bir sonuç verebileceğini belirtmek için yöntem adının başına Try veya TryGet veya TryFind koydum (örneğin, TryFindCustomer, TryLoadFile, vb.).

Bu, arayan kişinin sonuç üzerinde toplama boru hattı oluşturma (bkz. Martin Fowler'in Koleksiyon Boru Hattı ) gibi farklı teknikler uygulamasını sağlar .

Kod karmaşıklığını azaltmak için null yerine Option döndürmenin kullanıldığı başka bir örnek: Siklomatik Karmaşıklık Nasıl Azaltılır: Seçenek İşlev Türü


1
Bir cevap yazdım, yukarı kaydırdığımda size benzediğini görebiliyorum ve kabul ediyorum, 0 veya 1 elemanlı genel bir koleksiyonla bir seçenek türü uygulayabilirsiniz. Ek bağlantılar için teşekkürler.
Gabriel P.

1

Öğütmek için daha fazla et: DAL'imin bazılarının önerdiği gibi GetPersonByID için NULL döndürdüğünü varsayalım. NULL alırsa (oldukça ince) BLL'm ne yapmalı? Bu NULL değerini iletin ve son tüketicinin bu konuda endişelenmesine izin verin (bu durumda bir ASP.Net sayfası)? BLL'nin bir istisna atmasına ne dersiniz?

BLL ASP.Net ve Win App veya başka bir sınıf kütüphanesi tarafından kullanılıyor olabilir - son tüketicinin kendiliğinden GetPersonByID yönteminin bir boş döndürdüğünü bilmesini beklemenin haksız olduğunu düşünüyorum (boş türler kullanılmadıkça, sanırım ).

Benim almam (değdiğine göre), hiçbir şey bulunmazsa DAL'ım NULL döndürür. BAZI NESNELER İÇİN, sorun değil - 0 olabilir: pek çok şey listesi, bu yüzden herhangi bir şeye sahip olmamak iyidir (örneğin, favori kitapların bir listesi). Bu durumda, BLL'm boş bir liste döndürür. Çoğu tek varlık için (örneğin, kullanıcı, hesap, fatura), bir tane yoksa, bu kesinlikle bir problemdir ve pahalı bir istisna atmaktadır. Ancak, daha önce uygulama tarafından verilen benzersiz bir tanımlayıcı ile bir kullanıcı almayı görmek her zaman bir kullanıcı döndürmelidir, istisna olduğu gibi istisna "uygun" bir istisnadır. BLL'nin (ASP.Net, f'rinstance) son tüketicisi, işlerin sadece iri-dory olmasını beklediğinden, GetPersonByID'ye yapılan her bir çağrıyı bir try-catch bloğuna sarmak yerine bir İşlenmemiş İstisna İşleyicisi kullanılacaktır.

Yaklaşımımda göze çarpan bir sorun varsa, lütfen her zaman öğrenmeye hevesli olduğum için bana bildirin. Diğer posterlerin söylediği gibi, istisnalar maliyetli şeylerdir ve "ilk kontrol" yaklaşımı iyidir, ancak istisnalar sadece bu olmalıdır - istisnai.

Ben bu yazı zevk, çok "iyi bağlıdır" senaryolar için iyi öneriler :-)


Ve elbette, bugün BLL'mden NULL döndüreceğim bir senaryo ile karşılaştım ;-) Bu, hala bir istisna atabilir ve tüketen sınıfımda dene / yakala kullanabilirim ama hala bir sorunum var : Tüketim sınıfım nasıl bir try / catch kullanmayı biliyor, benzer NULL kontrol etmek için nasıl biliyorlar?
Mike Kingscott

Bir yöntemin @ throws doctag aracılığıyla bir istisna attığını ve @return doctag öğesinde null döndürebileceği gerçeğini belgeleyebilirsiniz.
timoxley

1

Bence kod tabanınızın sağlığı için fonksiyonlar boş olmamalıdır. Birkaç sebep düşünebilirim:

Boş referansı işleyen büyük miktarda koruma maddesi olacaktır if (f() != null).

Nedir, nullkabul edilmiş bir cevap mı yoksa sorun mu? Null, belirli bir nesne için geçerli bir durum mu? (kod için bir istemci olduğunuzu düşünün). Yani tüm referans türleri boş olabilir, ama olmalı mı?

Having nullkodunuzu-baz büyüdükçe asılı etrafta hemen hemen her zaman zaman zaman bir kaç beklenmedik NullRef istisnalar verecektir.

İşlevsel programlamanın bazı çözümleri tester-doer patternveya uygulanması vardır option type.


0

Ben bir iki IsItThere () "yöntemi ve bir" GetItForMe () "yöntemi ve bu nedenle bir yarış koşulu yol açar iki (tüm web) cevap sayısı çok şaşırdım. Null döndüren, bir değişkene atayan ve değişkeni Null için tek bir testte kontrol eden bir işlevin sorunu nedir? Eski C kodum biberliydi

if (NULL! = (değişken = işlev (bağımsız değişkenler ...))) {

Böylece bir değişkendeki değeri (veya null) ve sonucu bir kerede elde edersiniz. Bu deyim unutuldu mu? Neden?


0

Buradaki birçok gönderiye katılıyorum. null .

Benim mantık nullable olmayan özellikleri ile boş bir nesne oluşturmak hatalara neden olabilir olmasıdır. Örneğin, bir int IDmülke sahip bir varlığın başlangıç ​​değeri, ID = 0tamamen geçerli bir değerdir. Bu nesne, bazı durumlarda veritabanına kaydedilseydi, kötü bir şey olurdu.

Yineleyici olan her şey için her zaman boş koleksiyonu kullanırdım. Gibi bir şey

foreach (var eachValue in collection ?? new List<Type>(0))

Bence kod kokusu. Koleksiyon özellikleri hiç boş olmamalıdır.

Bir kenar kasa String. Birçok insan, String.IsNullOrEmptygerçekten gerekli olmadığını söylüyor, ancak boş bir dize ile null arasında her zaman ayrım yapamazsınız. Ayrıca, bazı veritabanı sistemleri (Oracle) aralarında ayrım yapmaz ( ''olarak depolanır DBNULL), bu yüzden onları eşit olarak işlemek zorunda kalırsınız. Ne Textboxes ne de çoğu değişim formatları için farklı temsillerini varken Bunun nedeni, çoğu dize değerleri, ya kullanıcı girişi veya harici sistemlerden gelen edilir ''ve null. Bu nedenle, kullanıcı bir değeri kaldırmak istese bile, giriş denetimini temizlemekten başka bir şey yapamaz. Ayrıca, DBMS'niz nvarcharoracle değilse nullable ve nullable olmayan veritabanı alanlarının ayrımı sorgulanabilir değildir - izin veren zorunlu bir alan''garip, kullanıcı arayüzünüz buna asla izin vermez, bu nedenle kısıtlamalarınız eşleşmez. Yani buradaki cevap, bence, onları her zaman eşit olarak ele almak.

İstisnalar ve performansla ilgili sorunuzla ilgili: Program mantığınızda tam olarak işleyemeyeceğiniz bir istisna atarsanız, bir noktada programınız ne yapıyorsa iptal etmeli ve kullanıcıdan ne yaptığını tekrar yapmasını istemelisiniz. Bu durumda, a'nın performans cezası catchgerçekten endişelerinizden en azıdır - kullanıcıya sormak zorunda kalmak odadaki filtir (yani tüm kullanıcı arayüzünü yeniden oluşturmak veya internet üzerinden HTML göndermek anlamına gelir). Dolayısıyla, " İstisnalarla Program Akışı " nın anti-desenini takip etmezseniz, rahatsız etmeyin, eğer mantıklıysa bir tanesini atın. "Doğrulama İstisnası" gibi sınırda olan durumlarda bile, performans gerçekten bir sorun değildir, çünkü her durumda kullanıcıya tekrar sormanız gerekir.


0

Bir Asenkron TryGet Desen:

Senkron yöntemleri için, inanıyorum @Johann Gerell en cevabı olan her durumda kullanmak için şablon.

Ancak outparametreyle TryGet kalıbı Async yöntemleriyle çalışmaz.

C # 7'nin Tuple Değişmezleriyle artık şunları yapabilirsiniz:

async Task<(bool success, SomeObject o)> TryGetSomeObjectByIdAsync(Int32 id)
{
    if (InternalIdExists(id))
    {
        o = await InternalGetSomeObjectAsync(id);

        return (true, o);
    }
    else
    {
        return (false, default(SomeObject));
    }
}
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.