null値をチェックする適切な方法は何ですか?


122

null結合型の演算子にデフォルト値を簡単に割り当てることができるため、null結合演算子が大好きです。

 int y = x ?? -1;

それは素晴らしいことですが、で簡単なことをする必要がある場合は例外ですx。たとえば、を確認したい場合、Session通常はもっと冗長なものを書く必要があります。

私はこれを行うことができればいいのに:

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

しかし.ToString()、nullチェックの前にが呼び出されるため、Session["key"]nullの場合は失敗するため、これはできません。私はこれをやる:

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

それは機能し、私の意見では、3行の代替よりも優れています。

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

それがうまくいくとしても、もっと良い方法があるかどうか私はまだ興味があります。私がいつも何Session["key"]回参照しなければならないかは関係ありません。1回はチェックのため、もう1回は割り当てのためです。何か案は?


20
これは、C#にGroovyの.?ような「安全なナビゲーションオペレーター」()が必要だったときです。
Cameron

2
@Cameron:これは、C#がnull許容型(参照型を含む)をモナドとして扱えるようにしたいので、「安全なナビゲーション演算子」は必要ありません。
Jon Purdy

3
null参照の発明者はそれを「10億ドルの間違い」と呼んでおり、私は同意する傾向があります。参照してくださいinfoq.com/presentations/...
ジェイミー井出

彼の実際の間違いは、ヌル可能型と非ヌルラベル型の安全でない(言語強制ではない)混合です。
MSalters 2012年

@JamieIde非常に興味深いリンクをありがとう。:)
BobRodes、2015年

回答:


182

どうですか

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

79
これで力が強い。
Chev

2
@Matthew:セッション値のタイプがObjectであるため、違います
BlackBear

1
@BlackBearが、キャストが有効であるので、返される値は、おそらく文字列である
フィーロ

これが私の質問に対する最も直接的な回答だったので、私は回答をマークしますが、ジョンスキートの拡張メソッド.ToStringOrDefault()が私の好ましい方法です。ただし、私はジョンの拡張メソッド内でこの回答を使用しています;)
Chev

10
私がこれを嫌うのは、セッションに他のタイプのオブジェクトが詰め込まれている場合、予想よりもプログラムの微妙なバグを隠している可能性があるためです。エラーが早く現れる可能性が高いと思うので、安全なキャストを使用したい。また、文字列オブジェクトでToString()を呼び出さないようにします。
tvanfosson 2012年

130

特にToString()これを頻繁に行う場合は、拡張メソッドを作成できます。

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

...

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

または、もちろんデフォルトを取るメソッド:

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

...

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

16
.ToStringOrDefault()シンプルでエレガンスです。素晴らしい解決策。
Chev

7
これには全く同意できません。上の拡張メソッドobjectは呪いでコードベースをジャンクアップし、null this値でエラーなしで動作する拡張メソッドは純粋に悪です。
Nick Larsen

10
@NickLarsen:節度のあるすべてのものだと私は言う。IMO-nullで機能する拡張メソッドは、それらが何をするかについて明確である限り、非常に便利です。
Jon Skeet

3
@ one.beat.consumer:うん。それがあった場合だけで、フォーマット(または任意のタイプミス)となって、それは一つのことだったでしょうが、メソッド名の著者の選択を変更すると、適切な編集がIMO、通常は何を超えています。
ジョンスキート2012年

6
@ one.beat.consumer:文法やタイプミスを修正する場合は問題ありません-しかし、誰か(私だけでなく誰でも)が意図的に選択した名前を変更すると、私には違った感じになります。その時点で、代わりにコメントでそれをお勧めします。
Jon Skeet

21

また、変換が失敗した場合asに生成さnullれるを使用することもできます。

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

これは戻ってくる"none"誰かが詰めても、int中にSession["key"]


1
これToString()は、そもそも必要がない場合にのみ機能します。
アベル

1
まだ誰もこの回答に反対票を投じていないことに驚いています。これは、意味的に、OPが実行したいこととは完全に異なります。
Timwi、2012年

@Timwi:OPはToString()、文字列を含むオブジェクトを文字列に変換するために使用します。obj as stringまたはでも同じことができます(string)obj。これは、ASP.NETではかなり一般的な状況です。
Andomar 2012年

5
@Andomar:いいえ、OPは彼が言及しなかったタイプのToString()オブジェクト(つまりSession["key"])を呼び出しています。これは、文字列である必要はなく、あらゆる種類のオブジェクトである可能性があります。
Timwi、2012年

13

常にであるstring場合は、キャストできます。

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

これには、誰かがにint何かを詰め込んだ場合に間違いを隠す代わりに不平を言うという利点がありSession["key"]ます。;)


10

提案された解決策はすべて適切であり、質問に答えます。これは少し拡張するだけです。現在、回答の大部分はnull検証と文字列型のみを扱います。StateBagオブジェクトを拡張してジェネリックを含めることができますGetValueOrDefaultJon Skeetが投稿した回答と同様にメソッド。

文字列をキーとして受け入れ、セッションオブジェクトを型チェックする単純な汎用拡張メソッド。オブジェクトがnullまたは同じタイプでない場合はデフォルトが返され、それ以外の場合はセッション値が厳密に型指定されて返されます。

このようなもの

/// <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
この拡張メソッドの使用例を含めることができますか?StateBagはビューステートを処理し、セッションは処理しませんか?私はASP.NET MVC 3を使用しているため、実際にはビューステートに簡単にアクセスできません。伸ばしたいと思いますHttpSessionState
Chev

この回答では、成功した場合、値3xおよび2キャストを取得する必要があります。(私はそれが辞書であることを知っていますが、初心者は高価な方法で同様の方法を使用するかもしれません。)
ジェイク・バーガー

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

1
@jberger "as"を使用した値へのキャストは、ブールなどの値を返す可能性があるため、ジェネリック型にクラス制約がないため、アクセスできません。@AlexFord申し訳ありませんがHttpSessionState、セッションを延長したいと思います。:)
Richard

確かに。リチャードが指摘したように、制約が必要です。(...値型を使用したい場合は別の方法)
Jake Berger

7

と呼ばれるメソッドを使用します NullOr

使用法

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

ソース

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

はい、これは意図された問題に対するより一般的な答えです-あなたは私に勝ちました-そして安全なナビゲーションの候補です(単純なもののラムダを気にしない場合)-しかし、それでもまだ書くのは少し面倒です、上手 :)。個人的に私はいつも選ぶつもりですか?:代わりに(高価でない場合は、とにかくそれが再配置される場合)...
NSGaga-mostly-active 12/03/26

...そして、「命名」はこの問題の真の問題です-何も正しく描写していない(または「追加」しすぎている)か、長いものではありませんまだ??)-「プロパティ」または「安全」は私が使用したものです。value.Dot(o => o.property)?? @デフォルト多分?
NSGaga-mostly-active 12/03/26

@NSGaga:私たちはかなり長い間、名前を行ったり来たりしていました。検討しましDotたが、説明が多すぎることがわかりました。私たちは、NullOr自己説明と簡潔さの間の適切なトレードオフとして解決しました。ネーミングがまったく気にならなかった場合は、いつでも呼び出すことができます_。ラムダを書くのが面倒なので、スニペットを使用できますが、個人的には非常に簡単です。については? :、より複雑な式では使用できません。新しいローカルに移動する必要があります。NullOrそれを避けることができます。
Timwi、2012年

6

私の好みは、キーが格納されているオブジェクトが1つでない場合に備えて、安全な文字列へのキャストを使用することです。を使用してToString()も、期待した結果が得られない場合があります。

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

@Jon Skeetが言うように、これをたくさん行う場合は、拡張メソッド、またはより適切には、強く型付けされたSessionWrapperクラスと組み合わせた拡張メソッドを使用します。拡張メソッドがなくても、強く型付けされたラッパーは良い考えかもしれません。

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

使用されます

 var session = new SessionWrapper(Session);

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

3

補助関数を作成する

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


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

2

スキートの答えは最高です-特に私は彼ToStringOrNull()が非常にエレガントであなたのニーズに最もよく合っていると思います。拡張メソッドのリストにオプションをもう1つ追加したいと思います。

元のオブジェクトまたはnullのデフォルトの文字列値を返します

// 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");

var元の入力のタイプとして返​​されるため、戻り値に使用します。デフォルトの文字列としてのみnull


null defaultValue必要がない場合(つまりinput != null)に例外をスローするのはなぜですか?
Attila

input != nullevalのは、それ自体としてオブジェクトを返します。input == nullパラメータとして提供された文字列を返します。したがって、人が電話をかける可能性はあります.OnNullAsString(null)が、目的は(めったに役立つ拡張メソッドではありませんが)オブジェクトまたはデフォルトの文字列を取得することでした... nullになることはありません
one.beat.consumer

input!=null場合シナリオは、入力のみを返します。defaultValue!=nullまた、保持しています。それ以外の場合はをスローしArgumentNullExceptionます。
Attila

0

これは、サポートされていないバージョンの.NET用の小さなタイプセーフな「Elvis演算子」です。

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

最初の引数はテストされるオブジェクトです。2つ目は関数です。3番目はnull値です。だからあなたの場合:

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

null許容型にも非常に役立ちます。例えば:

decimal? v;
...
IsNull.Substitute(v,v.Value,0);
....
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.