C# 我如何管理空检查的冲击?
在编程中,我们经常会遇到C# 我如何管理空检查的冲击?,c#,.net,C#,.net,在编程中,我们经常会遇到null检查以特别大的数量出现的情况。我说的是: if (doc != null) { if (doc.Element != null) { ... and so on } else throw new Exception("Element cannot be null"); } else { throw new Exception("document cannot be null"); } 基本上,整件事都变成了一场无法读懂的噩梦,
null
检查以特别大的数量出现的情况。我说的是:
if (doc != null)
{
if (doc.Element != null)
{
... and so on
}
else
throw new Exception("Element cannot be null");
} else {
throw new Exception("document cannot be null");
}
基本上,整件事都变成了一场无法读懂的噩梦,所以我想知道:有没有更简单的方法来描述我在上面试图做的事情?(除了空检查之外,我还不时得到类似string.IsNullOrEmpty
的东西。)
接受答案:我接受了答案,因为所述方法具有创新性,正是我想要的。谢谢你,肖恩 那个代码示例几乎是不可读的。。。当变量可能为null时,必须检查null。但是,如果您想减少这种情况,请确保返回对象的方法从不返回null,而是始终返回完全有效且构造的对象。让它在返回null的情况下抛出异常。返回null或-1或其他一些奇怪的约定不应该代替错误处理。如果您只是想抛出异常,为什么不让语言运行库为您抛出它呢
doc.Element.whatever("foo");
您仍然会得到一个NullPointerException(或C#中的任何内容),其中包含完整的回溯信息。将它们向前推到函数的开头,并将它们排除在执行该工作的代码部分之外。像这样:
if (doc == null)
throw new ArgumentNullException("doc");
if (doc.Element == null)
throw new ArgumentException("Element cannot be null");
AndSoOn();
单独(静态?)函数调用:
public static void CheckForNullObject( object Obj, string Message) {
if(Obj == null){
throw new Exception(Message);
}
}
虽然这不是最好的选择,但它会更具可读性。如果属性被初始化为适当的值,那么类的构造函数应该只创建对象吗?也就是说,只有当实例在创建时具有最少数量的属性时,才会创建实例,而不是创建一个验证(Doc-Doc)方法来执行基本相同的操作,即检查对象的有效性。您可能对该实例感兴趣 在过去,它帮助我在很多情况下消除了空检查 示例(C++)
除非您能够对异常进行智能处理,否则不要捕获异常 在这种情况下,您的异常处理程序与默认值相比几乎没有什么价值——也就是说,让异常传播回调用链 在应用程序/线程的顶层,应该始终有异常处理程序来处理这些未捕获的异常 编辑:被否决了,我觉得被误解了,也许我太敏感了;-)。原始海报抛出的异常没有任何价值。它们既不能帮助最终用户,也不能帮助开发人员 应用程序中的顶级异常处理程序应该捕获这样的异常并记录它。日志应该包括堆栈跟踪。这将告诉开发人员错误来自何处,并消除许多实际上没有任何用途的代码行
如果异常增加了一些值,那么我同意抛出它是合理的。但事实并非如此。更糟糕的是,一旦你声明这是一个好的原则,你会看到更多的代码检查空引用,以至于代码会被它们弄得乱七八糟。如果一个典型的
NullReferenceException
就可以了,不要麻烦检查,让运行时抛出它。如果您需要添加上下文错误信息,出于日志记录或调试目的(或其他任何目的),请继续并将您的验证考虑到不同的方法中。在这种情况下,我仍然鼓励您抛出一个嵌套了原始异常的NullReferenceException
当我被迫手动浏览深层XML文档时,我也不得不做类似的事情
通常,我试图在类中的接口边界强制执行空正确性。然后,我可以忽略我的私有方法中的空检查。如果您觉得它由于嵌套的ifs而无法读取,我的建议是这样重写:
if (doc == null)
{
throw new Exception("document cannot be null");
}
if (doc.Element == null)
{
throw new Exception("Element cannot be null");
}
doc.Element.someMethod()
你可以写:
if (doc == null && doc.Element == null
{
}
但是您在解决方案的粒度上比我的帖子高一个帖子(例如,在异常情况下会出现单独的消息)。请查看“”
它满足了您对在C#中执行详细空检查的可读方式的需求
还有来解决那些主张允许运行时抛出NullReferenceException的人: 我讨论了主动抛出ArgumentNullException还是让运行时抛出NullReferenceException的问题。基本的共识是,采取主动的方法而不是NullReferenceException方法是个好主意。我并不是说他们是对的,而在这里鼓吹其他观点的人是错的。我只是说社区可能不同意你
我想指出的是,如果你做了大量这类检查,很有可能你做错了什么。要么你的方法做得太多,要么你传递了太多的“tramp”参数(这些参数除了传递给另一个函数之外没有其他用途)。也许你应该考虑是否可以将代码分解成更多的方法,或者将一些参数封装到一个对象中。 有几个选项(有些已经被别人提到),所以我只是添加到列表中。
private static void ThrowIfNull(this object o, string message) {
if (o != null) return;
throw new ArgumentNullException(message);
}
您可能对感兴趣: Spec#是一种用于API契约的正式语言,它使用用于 非空类型、前置条件、后置条件、对象不变性
private static void ThrowIfNull(this object o, string message) {
if (o != null) return;
throw new ArgumentNullException(message);
}
public static void Clear(int[]! xs) // xs is now a non-null type
{
for (int i = 0; i < xs.Length; i++)
{
xs[i] = 0;
}
}
int[] xs = null;
Clear(xs); // Error: null is not a valid argument
Assert.IsNotNull(doc, "document");
Assert.IsNotNull(doc.Element", "Element");
//... and so on
public static T CheckedGet<T>(Expression<Func<T>> expr) where T : class
{
CheckAccess(expr);
return expr.Compile().Invoke();
}
public static void CheckAccess(Expression expr)
{
switch (expr.NodeType)
{
case ExpressionType.Lambda:
CheckAccess((expr as LambdaExpression).Body);
break;
case ExpressionType.MemberAccess:
{
CheckAccess((expr as MemberExpression).Expression);
var value = Expression.Lambda(expr).Compile().DynamicInvoke();
if (value == null)
{
throw new NullReferenceException(expr.ToString());
}
}
break;
case ExpressionType.ArrayIndex:
{
var binaryExpr = expr as BinaryExpression;
CheckAccess(binaryExpr.Left);
var arrayLength = (int)Expression.Lambda(Expression.ArrayLength(binaryExpr.Left)).Compile().DynamicInvoke();
var arrayIndex = (int)Expression.Lambda(binaryExpr.Right).Compile().DynamicInvoke();
if (arrayIndex >= arrayLength)
{
throw new IndexOutOfRangeException(expr.ToString());
}
var value = Expression.Lambda(expr).Compile().DynamicInvoke();
if (value == null)
{
throw new NullReferenceException(expr.ToString());
}
}
break;
case ExpressionType.Constant:
return;
}
}
var val = CheckedGet(() => classA.PropertyA.ArrayB[0].FieldC);