Boş değerleri kontrol etmenin doğru yolu nedir?


122

Boş birleştirme operatörünü seviyorum çünkü null yapılabilir türler için varsayılan bir değer atamayı kolaylaştırıyor.

 int y = x ?? -1;

Basit bir şey yapmam gerekmedikçe bu harika x. Örneğin, kontrol etmek istersem Session, genellikle daha ayrıntılı bir şeyler yazmak zorunda kalırım.

Keşke bunu yapabilseydim:

string y = Session["key"].ToString() ?? "none";

Ama yapamazsınız çünkü .ToString()null kontrolden önce çağrılır, bu yüzden Session["key"]null ise başarısız olur . Bunu yapıyorum:

string y = Session["key"] == null ? "none" : Session["key"].ToString();

Bence işe yarıyor ve üç satırlık alternatiften daha iyi:

string y = "none";
if (Session["key"] != null)
    y = Session["key"].ToString();

Bu işe yarasa da daha iyi bir yol olup olmadığını hala merak ediyorum. Neye her zaman Session["key"]iki kez başvurmak zorunda olduğum önemli değil gibi görünüyor ; bir kez kontrol için ve tekrar ödev için. Herhangi bir fikir?


20
Bu, Groovy.? gibi C # 'da bir "güvenli navigasyon operatörü" ( ) olmasını dilediğim zamandır .
Cameron

2
@Cameron: Bu, C # 'nin null atanabilir türleri (referans türleri dahil) bir monad olarak ele almasını istediğim zamandır, böylece "güvenli gezinme operatörüne" ihtiyacınız olmaz.
Jon Purdy

3
Boş referansların mucidi bunu "milyar dolarlık hatası" olarak adlandırdı ve ben de aynı fikirdeyim. Bkz. İnfoq.com/presentations/…
Jamie Ide

Gerçek hatası, nullable ve nullabel olmayan türlerin güvenli olmayan (dile zorlanmayan) karıştırılmasıdır.
MSalters

@JamieIde Çok ilginç bir bağlantı için teşekkürler. :)
BobRodes

Yanıtlar:


182

Ne dersin

string y = (Session["key"] ?? "none").ToString();

79
Kuvvet Bununla güçlüdür.
Chev

2
@Matthew: Hayır, çünkü Oturum değerleri Object
BlackBear

1
@BlackBear ancak döndürülen değer büyük olasılıkla bir dizedir, bu nedenle atama geçerlidir
Firo

Bu, sorumun en doğrudan cevabıydı, bu yüzden cevabı işaretliyorum, ancak Jon Skeet'in uzatma yöntemi .ToStringOrDefault()tercih ettiğim yöntemdir . Ancak, bu cevabı Jon'un uzatma yönteminde kullanıyorum;)
Chev

10
Bundan hoşlanmıyorum çünkü oturumda beklediğinizden farklı türde bir nesne doldurulmuşsa, programınızda bazı ince hataları gizlemiş olabilirsiniz. Güvenli bir döküm kullanmayı tercih ederim çünkü hataları daha hızlı ortaya çıkaracağını düşünüyorum. Ayrıca, bir dize nesnesinde ToString () 'i çağırmaktan da kaçınır.
tvanfosson

130

Bunu sık sık özellikleToString() yapıyorsanız , bir uzatma yöntemi yazabilirsiniz:

public static string NullPreservingToString(this object input)
{
    return input == null ? null : input.ToString();
}

...

string y = Session["key"].NullPreservingToString() ?? "none";

Ya da elbette varsayılanı alan bir yöntem:

public static string ToStringOrDefault(this object input, string defaultValue)
{
    return input == null ? defaultValue : input.ToString();
}

...

string y = Session["key"].ToStringOrDefault("none");

16
.ToStringOrDefault()basit ve şık. Güzel bir çözüm.
Chev

7
Buna kesinlikle katılmıyorum. Uzantı yöntemleri objectbir lanettir ve bir kod tabanını gereksizdir ve boş thisdeğerlerde hatasız çalışan uzantı yöntemleri tamamen kötüdür.
Nick Larsen

10
@NickLarsen: Her şey ölçülü diyorum. Null ile çalışan uzatma yöntemleri, ne yaptıkları konusunda net oldukları sürece çok yararlı olabilir, IMO .
Jon Skeet

3
@ one.beat.consumer: Evet. O olsaydı sadece IMO, biçimlendirme (veya herhangi bir yazım hatası) bir şey olurdu, fakat normalde uygun düzenleme olanın ötesine yöntem adının bir yazarın seçimi değişen olmuştur.
Jon Skeet

6
@ one.beat.consumer: Dilbilgisini ve yazım hatalarını düzeltirken sorun değil - ancak birisinin (sadece benim değil) kasıtlı olarak seçtiği bir adı değiştirmek bana farklı geliyor. Bu noktada, bunun yerine bir yorumda öneririm.
Jon Skeet

21

Ayrıca kullanabilirsiniz as, hangi verim nulldönüşüm başarısız olursa:

Session["key"] as string ?? "none"

Bu dönecekti "none"birisi bir dolma bile intin Session["key"].


1
Bu yalnızca ToString()ilk etapta ihtiyacınız olmadığında işe yarar.
Abel

1
Henüz kimsenin bu cevaba olumsuz oy vermediğine şaşırdım. Bu, OP'nin yapmak istediğinden anlamsal olarak tamamen farklıdır.
Timwi

@Timwi: OP, ToString()bir dizge içeren bir nesneyi dizeye dönüştürmek için kullanır . Aynısını obj as stringveya ile yapabilirsiniz (string)obj. ASP.NET'te oldukça yaygın bir durumdur.
Andomar

5
@Andomar: Hayır, OP, türünden bahsetmediği ToString()bir nesneyi (yani Session["key"]) arıyor . Herhangi bir nesne olabilir, ille de bir dizge olmayabilir.
Timwi

13

Her zaman bir olacaksa string, şunları yapabilirsiniz:

string y = (string)Session["key"] ?? "none";

Bu, birisi bir intşey doldurursa, hatayı gizlemek yerine şikayet etme avantajına sahiptir Session["key"]. ;)


10

Önerilen tüm çözümler iyidir ve soruyu yanıtlar; bu yüzden bu sadece onu biraz genişletmek içindir. Şu anda yanıtların çoğu yalnızca boş doğrulama ve dize türleriyle ilgilenir. StateBagNesneyi GetValueOrDefault, Jon Skeet tarafından gönderilen cevaba benzer şekilde genel bir yöntemi içerecek şekilde genişletebilirsiniz .

Bir dizeyi anahtar olarak kabul eden ve ardından yazılan basit bir genel uzantı yöntemi, oturum nesnesini kontrol eder. Nesne boşsa veya aynı türde değilse, varsayılan döndürülür, aksi takdirde oturum değeri güçlü bir şekilde yazılan döndürülür.

Bunun gibi bir şey

/// <summary>
/// Gets a value from the current session, if the type is correct and present
/// </summary>
/// <param name="key">The session key</param>
/// <param name="defaultValue">The default value</param>
/// <returns>Returns a strongly typed session object, or default value</returns>
public static T GetValueOrDefault<T>(this HttpSessionState source, string key, T defaultValue)
{
    // check if the session object exists, and is of the correct type
    object value = source[key]
    if (value == null || !(value is T))
    {
        return defaultValue;
    }

    // return the session object
    return (T)value;
}

1
Bu uzantı yöntemi için bir kullanım örneği ekleyebilir misiniz? StateBag oturumla değil, görüntüleme durumuyla ilgilenmiyor mu? ASP.NET MVC 3 kullanıyorum, bu yüzden durumu görüntülemek için gerçekten basit erişimim yok. Sanırım uzatmak istiyorsun HttpSessionState.
Chev

bu cevap, başarılı olursa 3x ve 2 yayın değerini almayı gerektirir. (Bunun bir sözlük olduğunu biliyorum, ancak yeni başlayanlar pahalı yöntemler üzerinde benzer uygulamaları kullanabilir.)
Jake Berger

3
T value = source[key] as T; return value ?? defaultValue;
Jake Berger

1
@jberger "As" kullanarak değere çevrim, potansiyel olarak bool gibi bir değer döndürmek isteyebileceğiniz için genel tür üzerinde bir sınıf kısıtlaması olmadığından erişilemez. @AlexFord Özür dilerim, HttpSessionStateseansı uzatmak istersiniz . :)
Richard

aslında. richard'ın belirttiği gibi, kısıtlamayı gerektirir. (... ve değer türlerini kullanmak istiyorsanız başka bir yöntem)
Jake Berger

7

Adlı bir yöntem kullanıyoruz NullOr.

kullanım

// Call ToString() if it’s not null, otherwise return null
var str = myObj.NullOr(obj => obj.ToString());

// Supply default value for when it’s null
var str = myObj.NullOr(obj => obj.ToString()) ?? "none";

// Works with nullable return values, too —
// this is properly typed as “int?” (nullable int)
// even if “Count” is just int
var count = myCollection.NullOr(coll => coll.Count);

// Works with nullable input types, too
int? unsure = 47;
var sure = unsure.NullOr(i => i.ToString());

Kaynak

/// <summary>Provides a function delegate that accepts only value types as return types.</summary>
/// <remarks>This type was introduced to make <see cref="ObjectExtensions.NullOr{TInput,TResult}(TInput,FuncStruct{TInput,TResult})"/>
/// work without clashing with <see cref="ObjectExtensions.NullOr{TInput,TResult}(TInput,FuncClass{TInput,TResult})"/>.</remarks>
public delegate TResult FuncStruct<in TInput, TResult>(TInput input) where TResult : struct;

/// <summary>Provides a function delegate that accepts only reference types as return types.</summary>
/// <remarks>This type was introduced to make <see cref="ObjectExtensions.NullOr{TInput,TResult}(TInput,FuncClass{TInput,TResult})"/>
/// work without clashing with <see cref="ObjectExtensions.NullOr{TInput,TResult}(TInput,FuncStruct{TInput,TResult})"/>.</remarks>
public delegate TResult FuncClass<in TInput, TResult>(TInput input) where TResult : class;

/// <summary>Provides extension methods that apply to all types.</summary>
public static class ObjectExtensions
{
    /// <summary>Returns null if the input is null, otherwise the result of the specified lambda when applied to the input.</summary>
    /// <typeparam name="TInput">Type of the input value.</typeparam>
    /// <typeparam name="TResult">Type of the result from the lambda.</typeparam>
    /// <param name="input">Input value to check for null.</param>
    /// <param name="lambda">Function to apply the input value to if it is not null.</param>
    public static TResult NullOr<TInput, TResult>(this TInput input, FuncClass<TInput, TResult> lambda) where TResult : class
    {
        return input == null ? null : lambda(input);
    }

    /// <summary>Returns null if the input is null, otherwise the result of the specified lambda when applied to the input.</summary>
    /// <typeparam name="TInput">Type of the input value.</typeparam>
    /// <typeparam name="TResult">Type of the result from the lambda.</typeparam>
    /// <param name="input">Input value to check for null.</param>
    /// <param name="lambda">Function to apply the input value to if it is not null.</param>
    public static TResult? NullOr<TInput, TResult>(this TInput input, Func<TInput, TResult?> lambda) where TResult : struct
    {
        return input == null ? null : lambda(input);
    }

    /// <summary>Returns null if the input is null, otherwise the result of the specified lambda when applied to the input.</summary>
    /// <typeparam name="TInput">Type of the input value.</typeparam>
    /// <typeparam name="TResult">Type of the result from the lambda.</typeparam>
    /// <param name="input">Input value to check for null.</param>
    /// <param name="lambda">Function to apply the input value to if it is not null.</param>
    public static TResult? NullOr<TInput, TResult>(this TInput input, FuncStruct<TInput, TResult> lambda) where TResult : struct
    {
        return input == null ? null : lambda(input).Nullable();
    }
}

Evet, bu amaçlanan soruna daha genel bir cevap - beni yeniyorsun - ve güvenli navigasyon için bir aday (basit şeyler için lambda-lara aldırmazsan) - ama yine de yazmak biraz zahmetli, iyi :). Şahsen ben her zaman? : bunun yerine (pahalı değilse, yine de yeniden düzenleyin) ...
NSGaga-çoğunlukla aktif değil

... Ve 'Adlandırma' bununla ilgili gerçek sorundur - hiçbir şey gerçekten doğru gibi görünmüyor (veya çok fazla 'ekliyor') ya da uzun - NullOr iyi ama 'boş' IMO'ya (artı siz var ?? hala) - 'Mülk' veya 'Güvenli' kullandım. value.Dot (o => o.property) ?? @default belki?
NSGaga-çoğunlukla aktif değil

@NSGaga: Bir süre isim konusunda ileri geri gittik. Biz düşünün yaptı Dotama çok undescriptive buldum. NullOrKendini açıklama ve kısalık arasında iyi bir değiş tokuş olarak karar kıldık. İsimlendirmeyi gerçekten önemsemiyorsan, her zaman arayabilirsin _. Lambdaları yazamayacak kadar zahmetli bulursanız, bunun için bir pasaj kullanabilirsiniz, ancak şahsen ben bunu yeterince kolay buluyorum. Gelince ? :, bunu daha karmaşık ifadelerle kullanamazsınız, onları yeni bir yerele taşımanız gerekir; NullOrbundan kaçınmanıza izin verir.
Timwi

6

Bir kereye mahsus tercihim, anahtarla depolanan nesnenin bir olmaması durumunda dizeye güvenli bir atama kullanmak olacaktır. Kullanmak ToString()istediğiniz sonuçları vermeyebilir.

var y = Session["key"] as string ?? "none";

@Jon Skeet'in dediği gibi, eğer bunu çok fazla bir uzatma yöntemi veya daha iyisi, güçlü bir şekilde yazılmış bir SessionWrapper sınıfı ile birlikte bir uzatma yöntemi yaparken bulursanız. Uzantı yöntemi olmasa bile, güçlü bir şekilde yazılmış sarmalayıcı iyi bir fikir olabilir.

public class SessionWrapper
{
    private HttpSessionBase Session { get; set; }

    public SessionWrapper( HttpSessionBase session )
    {
        Session = session;
    }

    public SessionWrapper() : this( HttpContext.Current.Session ) { }

    public string Key
    {
         get { return Session["key"] as string ?? "none";
    }

    public int MaxAllowed
    {
         get { return Session["maxAllowed"] as int? ?? 10 }
    }
}

Olarak kullanıldı

 var session = new SessionWrapper(Session);

 string key = session.Key;
 int maxAllowed = session.maxAllowed;

3

yardımcı bir işlev yaratmak

public static String GetValue( string key, string default )
{
    if ( Session[ key ] == null ) { return default; }
    return Session[ key ].toString();
}


string y = GetValue( 'key', 'none' );

2

Skeet'in cevabı en iyisidir - özellikle de onun ToStringOrNull() oldukça zarif olduğunu ve ihtiyacınıza en uygun . Uzantı yöntemleri listesine bir seçenek daha eklemek istedim:

Null için orijinal nesneyi veya varsayılan dize değerini döndür :

// Method:
public static object OrNullAsString(this object input, string defaultValue)
{
    if (defaultValue == null)
        throw new ArgumentNullException("defaultValue");
    return input == null ? defaultValue : input;
}

// Example:
var y = Session["key"].OrNullAsString("defaultValue");

varDöndürülen değer için kullanın , çünkü orijinal girdinin türü olarak geri dönecektir, yalnızca varsayılan dize olaraknull


Gerekmiyorsa null defaultValue(yani input != null) neden bir istisna yapalım ?
Attila

Bir input != nulleval, nesneyi kendisi olarak döndürür. input == nullparametre olarak sağlanan dizeyi döndürür. bu nedenle, bir kişinin arayabilmesi mümkündür .OnNullAsString(null)- ancak amaç (nadiren kullanışlı olsa da uzantı yöntemi) nesneyi geri veya varsayılan dizeyi geri almanızı sağlamaktı ... asla null
one.beat.consumer

input!=nullEğer senaryo sadece girişi dönecektir defaultValue!=nullaynı şekilde geçerlidir; aksi takdirde bir ArgumentNullException.
Attila

0

Bu, .NET'in?

public class IsNull
{
    public static O Substitute<I,O>(I obj, Func<I,O> fn, O nullValue=default(O))
    {
        if (obj == null)
            return nullValue;
        else
            return fn(obj);
    }
}

İlk argüman test edilen nesnedir. İkincisi, işlevdir. Üçüncüsü boş değerdir. Yani davanız için:

IsNull.Substitute(Session["key"],s=>s.ToString(),"none");

Null yapılabilir türler için de çok kullanışlıdır. Örneğin:

decimal? v;
...
IsNull.Substitute(v,v.Value,0);
....
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.