Как правильно проверить нулевые значения? - PullRequest
122 голосов
/ 20 марта 2012

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

 int y = x ?? -1;

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

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

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

Но вы можете 't, потому что .ToString() вызывается перед проверкой на ноль, поэтому происходит сбой, если Session["key"] равен нулю.В итоге я делаю это:

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

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

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

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

Ответы [ 10 ]

183 голосов
/ 20 марта 2012

А как же

string y = (Session["key"] ?? "none").ToString();
130 голосов
/ 20 марта 2012

Если вы часто делаете это специально с 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");
21 голосов
/ 20 марта 2012

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

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

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

14 голосов
/ 20 марта 2012

Если это всегда будет string, вы можете разыграть:

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

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

10 голосов
/ 20 марта 2012

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

Простой общий метод расширения, который принимает строку в качестве ключа, а затем проверяет типобъект сеанса.Если объект имеет нулевое значение или не совпадает с типом, возвращается значение по умолчанию, в противном случае значение сеанса возвращается строго типизированным.

Примерно так

/// <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;
}
7 голосов
/ 22 марта 2012

Мы используем метод с именем 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();
    }
}
6 голосов
/ 20 марта 2012

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

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

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


string y = GetValue( 'key', 'none' );
2 голосов
/ 20 марта 2012

Ответ Скита самый лучший - в частности, я думаю, что его 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

0 голосов
/ 01 июня 2016

Это мой маленький безопасный тип "оператор Элвиса" для версий .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");

Это очень полезно для обнуляемых типов. Например:

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