C # 'da, boş bir nesnede bir uzantı yöntemi çağırdığınızda ne olur?


328

Yöntem null değerle çağrılıyor mu veya null başvuru istisnası veriyor mu?

MyObject myObject = null;
myObject.MyExtensionMethod(); // <-- is this a null reference exception?

Bu durumda asla 'this' parametresini null için kontrol etmem gerekmeyecek mi?


Tabii ki, bu hatayı atacak ASP.NET MVC ile ilgileniyorsunuz Cannot perform runtime binding on a null reference.
Mrchief

Yanıtlar:


386

Bu iyi çalışır (istisna yok). Uzantı yöntemleri sanal çağrıları kullanmaz (yani "callvirt" yerine "call" il komutunu kullanır), bu nedenle dahili mesaj yönteminde kendiniz yazmazsanız boş bir denetim olmaz. Bu aslında birkaç durumda yararlıdır:

public static bool IsNullOrEmpty(this string value)
{
    return string.IsNullOrEmpty(value);
}
public static void ThrowIfNull<T>(this T obj, string parameterName)
        where T : class
{
    if(obj == null) throw new ArgumentNullException(parameterName);
}

vb

Temel olarak, statik çağrılara yapılan çağrılar çok değişkendir - yani

string s = ...
if(s.IsNullOrEmpty()) {...}

dönüşür:

string s = ...
if(YourExtensionClass.IsNullOrEmpty(s)) {...}

burada hiç null kontrol yoktur.


1
Marc, “sanal” çağrılardan bahsediyorsun - ama aynı şey örnek yöntemlerindeki sanal olmayan çağrılar için de geçerli. Sanırım burada “sanal” sözcüğü yanlış yerleştirilmiş.
Konrad Rudolph

6
@Konrad: Bağlama göre değişir. C # derleyicisi, sanal olmayan yöntemler için bile callvirt'i kullanır ve tam olarak bir boş denetim elde eder.
Jon Skeet

Ben call ve callvirt il talimatları arasındaki farka atıfta bulunuyordum. Bir düzenlemede aslında iki Opcodes sayfasını href etmeye çalıştım, ancak editör bağlantılarda barfedildi ...
Marc Gravell

2
Bu uzatma yöntemlerinin kullanımının nasıl faydalı olabileceğini anlamıyorum. Bunun yapılabilmesi doğru olduğu anlamına gelmez ve aşağıda belirtildiği gibi İkili Worrier'ın bana daha az söylemek için bir sapma gibi görünüyor.
Tuzak

3
@Trap: İşlevsel tarzda programlama yapıyorsanız bu özellik harikadır.
Roy Tinker

50

Marc Gravell'den doğru cevaba ek.

Bu argümanın boş olduğu açıksa derleyiciden bir uyarı alabilirsiniz:

default(string).MyExtension();

Çalışma zamanında iyi çalışır, ancak uyarı üretir "Expression will always cause a System.NullReferenceException, because the default value of string is null".


32
Neden her zaman bir System.NullReferenceException neden uyarır? Aslında, asla olmayacak mı?
tpower

46
Neyse ki, biz programcılar uyarıları değil, sadece hataları önemsiyoruz: p
JulianR

7
@JulianR: Evet, bazıları değil, bazıları değil. Sürüm oluşturma yapılandırmamızda uyarıları hata olarak değerlendiriyoruz. Yani işe yaramıyor.
Stefan Steinegger

8
Not için teşekkürler; Bunu hata veritabanında alacağım ve C # 4.0 için düzeltebilir miyiz göreceğiz. (Vaat yok - gerçekçi olmayan bir köşe davası ve sadece bir uyarı olduğu için, düzeltmeye
başlayabiliriz

3
@Stefan: Bu bir hata ve "doğru" bir uyarı olmadığından, kodun sürümünüzü geçmesini sağlamak için uyarıyı bastırmak için #pragma deyimini kullanabilirsiniz.
Martin RL

24

Daha önce keşfettiğiniz gibi, genişletme yöntemleri sadece yüceltilmiş statik yöntemler olduğundan, atılanlar nullolmadan, aktarılan referanslarla çağrılırlar NullReferenceException. Ancak, arayan için örnek yöntemler gibi göründüklerinden, aynı şekilde davranmaları gerekir . Daha sonra, çoğu zaman thisparametreyi kontrol etmeli ve eğer bir istisna atamalısınız null. Yöntem, açık bir şekilde nulldeğerlerle ilgileniyorsa ve adı, aşağıdaki örneklerde olduğu gibi, gerektiği gibi gösteriyorsa, bunu yapmamak sorun değildir :

public static class StringNullExtensions { 
  public static bool IsNullOrEmpty(this string s) { 
    return string.IsNullOrEmpty(s); 
  } 
  public static bool IsNullOrBlank(this string s) { 
    return s == null || s.Trim().Length == 0; 
  } 
}

Bunun hakkında bir süre önce bir blog yazısı da yazdım .


3
@Marc Gravell'in cevabında açıklanan kullanımı da tercih ederken, doğru ve bana (ve iyi yazılmış) mantıklı olduğu için bunu oyladım.
qxotk

17

Uzantı yöntemine bir null iletilir.

Yöntem nesneye denetlemeden erişmeye çalışırsa null değeri varsa, evet, bir istisna atar.

Buradaki bir adam, "IsNull" ve "IsNotNull" uzantı yöntemlerini yazarak, referansın boş olup olmadığını kontrol etti. Şahsen bu bir sapma olduğunu düşünüyorum ve gün ışığı görmemiş olmalıydı, ama mükemmel geçerli c #.


18
Gerçekten, bana bir ceset "Hayatta mısın" diye sormak ve "hayır" cevabını almak gibi. Bir ceset herhangi bir soruya cevap veremez, null nesnede bir yöntemi "çağıramazsınız".
İkili Worrier

14
İkili Worrier mantığı ile aynı fikirde değilim, çünkü boş referanslar için endişelenmeden uzantıları arayabiliyor, ama analoji komedi değeri için +1 :-)
Tim Abell

10
Aslında, bazen birisinin öldüğünü bilmiyorsunuz, bu yüzden hala soruyorsunuz ve kişi "hayır, sadece gözlerim kapalıyken dinleniyor" diye cevap verebilir
nurchi

7
Birkaç işlemi zincirlemeniz gerektiğinde (3+ diyelim), (yan etkisi olmadığı varsayılarak) birkaç satır sondaj kazanı null kontrol kodunu "null-safe" uzatma yöntemleriyle zarif bir zincirleme tek astar haline getirebilirsiniz. (Önerilen ".?" - operatörüne benzer, ancak kuşkusuz zarif değil.) Bir uzantının "null-safe" olduğu açık değilse Genellikle yöntemin önüne "Safe" önekini eklerim, örneğin bir kopya- yönteminin adı "SafeCopy" olabilir ve bağımsız değişken null olursa null değerini döndürür.
AnorZaken

3
@BinaryWorrier cevabı ile çok zor güldüm hahahaha kendimi ölü olup olmadığını kontrol etmek için bir vücudu tekmelediğini gördüm hahaha Yani hayal gücümde, vücudun ölü olup olmadığını kontrol eden kim, vücudun kendisi değil, uygulama kontrol içimdeydi, hareket edip etmediğini görmek için onu tekmelemeye başladı. Öyleyse bir beden ölü olup olmadığını bilmez, WHO kontrol eder, bilir, şimdi vücuda ölü olup olmadığını söylemenin bir yolunu "ekleyebildiğinizi" ve bence ne olduğunu bir Uzantı içindir.
Zorkind

7

Diğerlerinin de işaret ettiği gibi, null referansta bir uzatma yöntemi çağırmak bu argümanın null olmasına neden olur ve özel bir şey olmaz. Bu, koruma cümleleri yazmak için genişletme yöntemlerini kullanma fikrini doğurur.

Örnekler için bu makaleyi okuyabilirsiniz: Siklomatik Karmaşıklığı Azaltma: Guard Clause Kısa sürüm şudur:

public static class StringExtensions
{
    public static void AssertNonEmpty(this string value, string paramName)
    {
        if (string.IsNullOrEmpty(value))
            throw new ArgumentException("Value must be a non-empty string.", paramName);
    }
}

Bu, null başvurusunda çağrılabilen dize sınıfı uzantı yöntemidir:

((string)null).AssertNonEmpty("null");

Çağrı, yalnızca çalışma zamanı uzantı yöntemini null başvuruda başarıyla çağıracağı için iyi çalışır. Ardından, dağınık sözdizimi olmadan koruma cümleleri uygulamak için bu uzantı yöntemini kullanabilirsiniz:

    public IRegisteredUser RegisterUser(string userName, string referrerName)
    {

        userName.AssertNonEmpty("userName");
        referrerName.AssertNonEmpty("referrerName");

        ...

    }

3

Extensionmethod statiktir, bu nedenle bu MyObject için herhangi bir şey yapmazsanız sorun olmamalı, hızlı bir test doğrulamalıdır :)


-1

Okunabilir ve dikey olmasını istediğinizde birkaç altın kural vardır.

  • Eiffel'den söylemeye değer bir şey, bir yönteme kapsüllenmiş belirli kodun bazı girdilere karşı çalışması gerektiğini, bazı önkoşulları karşıladığında ve beklenen bir çıktı sağladığında bu kodun çalışabileceğini söylüyor

Sizin durumunuzda - DesignByContract bozuldu ... boş bir örnekte mantık gerçekleştireceksiniz.

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.