Что такое правильный способ, чтобы проверить на NULL значения?


мне нравится оператор null-coalescing, потому что он позволяет легко назначить значение по умолчанию для типов с нулевым значением.

 int y = x ?? -1;

это здорово, если мне нужно сделать что-то простое с x. Например, если я хочу проверить Session, тогда мне обычно приходится писать что-то более подробное.

Я хотел бы сделать это:

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

но вы не можете, потому что .ToString() вызывается перед нулевой проверкой, поэтому она не выполняется, если Session["key"] имеет значение null. Я для этого:

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

это работает и лучше, на мой взгляд, чем трехстрочная альтернатива:

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

хотя это работает, мне все еще любопытно, есть ли лучший способ. Кажется, независимо от того, что я всегда должен ссылаться Session["key"] дважды: один раз для проверки, и снова на задание. Есть идеи?

10 121

10 ответов:

а как же

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

Если вы часто это делаете конкретно 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");

вы также можете использовать as, который дает null Если преобразование не удается:

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

это вернется "none" даже если кто-то набивается в int на Session["key"].

если это всегда будет string, можно привести:

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

это имеет преимущество жаловаться вместо того, чтобы скрывать ошибку, если кто-то набивает int или Session["key"]. ;)

все предложенные решения хороши, и ответьте на вопрос; так что это просто немного расширить его. В настоящее время большинство ответов имеют дело только с нулевой проверкой и строковыми типами. Вы могли бы расширить StateBag объект для включения общего GetValueOrDefault метод, похожий на ответ, опубликованный Джоном скитом.

простой универсальный метод расширения, который принимает строку в качестве ключа, а затем проверяет тип объекта сеанса. Если объект имеет значение 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;
}

мы используем метод, называемый 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();
    }
}

мое предпочтение, для одного прочь, было бы использовать безопасное приведение к строке в случае, если объект, хранящийся с ключом не один. Используя 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;

создать вспомогательную функцию

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


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

ответ скита является лучшим - в частности, я думаю, что его ToStringOrNull() - Это довольно элегантный и соответствует вашим потребностям лучших. Я хотел добавить еще один вариант в список методов расширения:

возвращает исходный объект или строковое значение по умолчанию для 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

Это мой маленький типобезопасный "оператор Элвиса" для версий .NET, которые не поддерживают ?.

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

первый аргумент-это тестируемый объект. Во-вторых, это функция. И третье-это нулевое значение. Так что для вашего случая:

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

Это очень полезно для nullable типов тоже. Например:

decimal? v;
...
IsNull.Substitute(v,v.Value,0);
....