Parametreleri C # /. NET'te DEĞİL olarak işaretle?


99

nullC # /. NET'te geçirilmesini engelleyen bir işlev parametresine atayabileceğim basit bir öznitelik veya veri sözleşmesi var mı? İdeal olarak bu, gerçek değerin nullkendisi için herhangi bir yerde ve çalışma zamanı atışında kullanılmadığından emin olmak için derleme zamanında da kontrol eder ArgumentNullException.

Şu anda şöyle bir şey yazıyorum ...

if (null == arg)
  throw new ArgumentNullException("arg");

... olmamasını beklediğim her argüman için null.

Aynı notta, Nullable<>aşağıdakilerin başarısız olacağı bir tersi var mı :

NonNullable<string> s = null; // throw some kind of exception

7
C #’ın son sürümlerinde "nameof ()" kullanımı daha iyi sonuç verir: yeni ArgumentNullException (nameof (arg)); Bu şekilde, isminizi yeniden
düzenlerseniz

C # 8 artık projenizde açabileceğiniz bir derleyici seçeneği olan null yapılabilir başvuru türlerini destekliyor. docs.microsoft.com/en-us/dotnet/csharp/nullable-references
Paul Stegler

Yanıtlar:


69

Maalesef derleme zamanında hiçbir şey mevcut değil.

Son zamanlarda blogumda yayınladığım, yeni bir yapı ve dönüşüm kullanan biraz hacky çözümüm var.

Kod Sözleşmeleri ile .NET 4.0'da hayat çok daha güzel olacak. Gerçek dil sözdizimine sahip olmak ve sıfırlanamazlık konusunda destek almak yine de oldukça güzel olurdu, ancak kod sözleşmeleri çok yardımcı olacaktır.

Ayrıca MiscUtil'de ThrowIfNull adında bir genişletme yöntemim var, bu da onu biraz daha basit hale getiriyor.

Son bir nokta - " if (null == arg)" yerine " " kullanmanızın herhangi bir nedeni var if (arg == null)mı? İkincisini okumayı daha kolay buluyorum ve ilkinin C'de çözdüğü problem C # için geçerli değil.


2
> Maalesef derleme zamanında mevcut hiçbir şey yok. > ...> Gerçek dil sözdizimine sahip olmak ve boş değer atanamazlık konusunda destek almak yine de oldukça güzel olurdu. Ayrıca derleme zamanı hatalarının ortaya çıktığını görmek istiyorum.
AndrewJacksonZA

2
@Jon, tanımlanmış bool için örtük bir çevrim varsa "if (arg = null)" çalışmaz mı? Sapkın görünebileceğini kabul ediyorum, ama derleme yapıyor ...
Thomas S. Trias

11
@ ThomasS.Trias: Evet, bu inanılmaz karanlık kenar durumunda, kombine bu kodu etrafında testlerin eksikliği ile birlikte bir yazım hatası var olmak, bir sorun ile bitirmek istiyorum. Bu noktada sanırım daha büyük sorunlarınız var :)
Jon Skeet

4
@Jon: Verildi. Sanırım sevgili Yoda şartlarımdan vazgeçeceğim. :-)
Thomas S. Trias

1
@SebastianMach: Bunun nedeninin if (null == x)doğal dil farklılıklarından kaynaklandığına inanmıyorum . Bunun gerçekten örneklerle yayılan eski bir stille ilgili olduğuna inanıyorum.
Jon Skeet

23

Bu soruya inanılmaz derecede geç kaldığımı biliyorum, ancak C #'ın en son büyük yinelemesinin piyasaya sürülmeye yaklaşması ve ardından yayınlanmasıyla cevabın alakalı hale geleceğini hissediyorum. C # 8.0'da büyük bir değişiklik olacak, C # tüm türlerin boş olmadığını varsayacaktır .

Mads Torgersen'e göre:

Sorun, boş referansların çok kullanışlı olmasıdır. C # 'da, her başvuru türünün varsayılan değeridirler. Varsayılan değer başka ne olabilir? Siz ona başka ne atayacağınıza karar verene kadar bir değişkenin başka hangi değeri olurdu? Siz onu doldurana kadar, yeni tahsis edilmiş bir referans dizisini başka hangi değeri ortaya koyabiliriz?

Ayrıca bazen boş, kendi başına mantıklı bir değerdir. Bazen, diyelim ki bir alanın bir değeri olmadığı gerçeğini temsil etmek istersiniz. Bir parametre için "hiçbir şey" geçmenin sorun olmadığını. Vurgu bazen üzerindedir. Ve burada sorunun başka bir kısmı yatıyor: C # gibi diller, tam burada bir boşluğun iyi bir fikir olup olmadığını ifade etmenize izin vermez.

Mads tarafından özetlenen çözüm şudur:

  1. Bir referansın boş kalmamasını istemenin daha yaygın olduğuna inanıyoruz. Null atanabilir başvuru türleri daha nadir tür olacaktır (ancak bize ne kadar olduğunu söyleyecek iyi verilerimiz olmasa da), bu nedenle yeni bir açıklama gerektirenler bunlar.

  2. Dil, null yapılabilir değer türleri için bir nosyon ve sözdizimine zaten sahiptir. İkisi arasındaki benzerlik, dil eklemeyi kavramsal olarak daha kolay ve dilbilimsel olarak daha basit hale getirecektir.

  3. İstediğinize aktif olarak karar vermedikçe, kendinize veya tüketicinize hantal boş değerlerle yük olmamanız doğru görünüyor. Null'lar, yokluğu değil, açıkça seçmeniz gereken şey olmalıdır.

İstenen özelliğe bir örnek:

public class Person
{
     public string Name { get; set; } // Not Null
     public string? Address { get; set; } // May be Null
}

Önizleme, Visual Studio 2017, 15.5.4+ önizleme için kullanılabilir.


1
Bunun C # 8.0'ın bir parçası olup olmadığını bilen var mı?
TroySteven

@TroySteven Evet, Visual Studio'daki ayarlardan olsa da buna katılmanız gerekiyor.
Greg

@TroySteven İşte bununla ilgili belgeler. docs.microsoft.com/en-us/dotnet/csharp/nullable-references
Greg

Güzel, çalışmaya başlamadan önce onu etkinleştirmeniz gerekiyor gibi görünüyor, geriye dönük uyumluluk için güzel.
TroySteven

15

Bunun ÇOK eski bir soru olduğunu biliyorum, ama bu soru burada eksikti:

ReSharper / Rider kullanıyorsanız, Annotated Framework'ü kullanabilirsiniz .

Düzenleme : Bu cevap için az önce rastgele bir -1 aldım. Bu iyi. Sadece o farkında hala o # 8.0 + projeleri artık C için tavsiye edilen yaklaşım olmadığı halde, geçerli (bkz anlamak için Greg'in cevabı ).


1
İlginç bir şekilde, derlenmiş uygulamanız varsayılan olarak JetBrains.Annotations.dll'ye başvuruda bulunmayacaktır, bu nedenle onu uygulama ile dağıtmanıza gerek kalmaz: ReSharper denetimlerini iyileştirmek için JetBrains Ek Açıklamaları nasıl kullanılır
Lu55

9

Kurumsal kitaplıktaki doğrulayıcılara göz atın. Şunun gibi bir şey yapabilirsiniz:

private MyType _someVariable = TenantType.None;
[NotNullValidator(MessageTemplate = "Some Variable can not be empty")]
public MyType SomeVariable {
    get {
        return _someVariable;
    }
    set {
        _someVariable = value;
    }
}

Ardından, doğrulamak istediğinizde kodunuzda:

Microsoft.Practices.EnterpriseLibrary.Validation.Validator myValidator = ValidationFactory.CreateValidator<MyClass>();

ValidationResults vrInfo = InternalValidator.Validate(myObject);

0

en güzeli değil ama:

public static bool ContainsNullParameters(object[] methodParams)
{
     return (from o in methodParams where o == null).Count() > 0;
}

ContainsNullParameters yönteminde de daha yaratıcı olabilirsiniz:

public static bool ContainsNullParameters(Dictionary<string, object> methodParams, out ArgumentNullException containsNullParameters)
       {
            var nullParams = from o in methodParams
                             where o.Value == null
                             select o;

            bool paramsNull = nullParams.Count() > 0;


            if (paramsNull)
            {
                StringBuilder sb = new StringBuilder();
                foreach (var param in nullParams)
                    sb.Append(param.Key + " is null. ");

                containsNullParameters = new ArgumentNullException(sb.ToString());
            }
            else
                containsNullParameters = null;

            return paramsNull;
        }

Tabii ki bir engelleyici veya yansıtma kullanabilirsiniz, ancak bunları takip etmek / kullanmak çok az ek yük ile kolaydır


3
Bu tür sorguları kullanarak çok kötü bir uygulama: return (from o in methodParams where o == null).Count() > 0; Kullanım: return methodParams.Any(o=>o==null);büyük koleksiyonlarda çok daha hızlı olacak
Yavanosta

Neden istisnayı geçsin? Neden fırlatmıyorsun?
Martin Capodici

-4

Tamam bu cevap biraz geç, ama işte bunu nasıl çözüyorum:

public static string Default(this string x)
{
    return x ?? "";
}

Bu uzatma yöntemini kullanın, sonra boş ve boş dizgeyi aynı şey olarak değerlendirebilirsiniz.

Örneğin

if (model.Day.Default() == "")
{
    //.. Do something to handle no Day ..
}

İdeal değil biliyorum, çünkü her yerde varsayılanı aramayı hatırlamanız gerekiyor ama bu bir çözüm.


5
nasıl bu (x == null) çeklerden daha iyi / daha kolay? veya String.IsNotNullOrEmpty işlevi.
Batavia

Önem string.IsNotNullOrEmptyverdiğiniz şeye sol tarafta sahip olmanın şekerinden çok daha iyi değil . Diğer işlevlere (uzunluk, birleştirme) vb. Beslenebilir. Marjinal olarak daha iyi.
Martin Capodici

@MartinCapodici Ayrıca, bu, null ( ) Default()üzerinde örnek method ( ) 'u çağırmanın güvenli olduğuna dair bir yanılsama yaratır . Uzatma yöntemlerinin boşa karşı kontrol edilmediğini biliyorum, ancak gözlerim farkında değil ve kodu zaten hayal ettiler : :)model.Day?model?.Day?.Default()
Alb
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.