What is the proper way to check for null values?

我喜欢空合并运算符,因为它可以很容易地为可空类型分配默认值。

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

在我看来,它比三行替代方案更有效,也更好:

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

即使这种方法有效,我还是很好奇是否有更好的方法。似乎无论如何,我总是要引用 Session["key"]两次; 一次是为了检查,一次是为了作业。有什么想法吗?

10084 次浏览

关于什么

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

创建一个辅助函数

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




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

如果你经常这样做,那么你可以写一个扩展方法:

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

如果总是 string,你可以选择:

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

这有抱怨的优势,而不是隐藏错误,如果有人填充 int或东西在 Session["key"]。;)

所有建议的解决方案都是好的,并且回答了这个问题; 所以这只是对它的一点延伸。目前大多数答案只处理 null 验证和字符串类型。您可以扩展 StateBag对象以包含一个通用的 GetValueOrDefault方法,类似于 Jon 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;
}

斯基特的回答是最好的-特别是我认为他的 ToStringOrNull()是相当优雅的,并适合你的需要最好的。我想在扩展方法列表中再添加一个选项:

返回 无效的原始对象或默认字符串值:

// 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时才作为默认字符串返回

您还可以使用 as,如果转换失败,它将生成 null:

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

这将返回 "none",即使有人在 Session["key"]中填充了 int

我的偏好是使用安全的强制转换来进行字符串转换,以防存储在密钥中的对象不是字符串。使用 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;

我们使用一种称为 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();
}
}

这是我的小型类型安全“猫王操作符”的版本的.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);
}
}

First argument is the tested object. Second is the function. And third is the null value. So for your case:

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

对于可为空的类型也非常有用,例如:

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