如何覆盖NullReferenceException行为以获得更好的信息性错误?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何覆盖NullReferenceException行为以获得更好的信息性错误?相关的知识,希望对你有一定的参考价值。
上下文
在Unity中,NullReferenceException仅记录错误,并且不会使IDE进入调试模式。游戏有时很难重建相同的环境,以便在100%的时间内发生错误。
组件变量被序列化并通过统一编辑器公开,设计人员经常忘记分配值,导致组件的成员变量为NULL的情况。
由于Unity的特殊情况,NullReferenceException是最常见的例外,我正在尝试探索如何使异常更加明显。
题
NullReferenceException消息非常无用。对我来说,当我看到NullReferenceException时,我甚至不知道哪个变量是NULL。
player.GetComponent<Movement>().AnothingVariable.CallFunction() // NullReferenceException, which part caused it?
当前错误消息:
NullReferenceException: Object reference not set to an instance of an object
理想的错误信息:
NullReferenceException: Trying to access CallFunction in type Movement, in GameObject "Player" from component "Controller".
如果异常没有帮助,那通常意味着您在一行中有太多命令。一个简单的解决方案是将代码分成更多行,使用临时变量来存储每个成员调用(函数,属性或字段)的结果。
这可能不会是性能损失。编译器和JiT编译器应该能够注意到这些变量在很大程度上是“无用的”并在发布版本中将其删除。您可以获得更好的读取和可调试代码,几乎没有惩罚。
据我所知,你无法改变这一点。如果您正在访问对象的属性并且它有可能为null,则应该对其进行null检查。您可以使用更现代版本的C#的新空检查功能
A?.B?.C?.D
如果A或B或C为null,则表达式返回null并且您不会获得异常。但是,您无法获得有关null的信息。据我所知,唯一的方法是明确检查参数并抛出自己的异常(如果这是您想要发生的事情)。
if (a == null)
{
throw new ArgumentNullException("a");
}
等于B和C
而不是试图找出什么是null
,更好的策略是防御性地针对这种情况进行编程。通常使用的方法是使用保护条款来确保变量不可能是null
。
实例化
可以将一个guard子句添加到对象的构造函数中,以确保字段不能包含null
。
public class SomeClass
{
// Marking fields readonly ensures they cannot be set
// (thus cannot be set to null) from anywhere outside
// of the constructor
public readonly string field1;
public readonly Type field2;
public SomeClass(string field1, Type field2)
{
// Throwing ArgumentNullException ensures the class
// can never be created with missing (null) values.
if (field1 == null)
throw new ArgumentNullException(nameof(field1));
if (field2 == null)
throw new ArgumentNullException(nameof(field2));
this.field1 = field1;
this.field2 = field2;
}
public string Field1
{
get { return field1; } // Never null
}
public Type Field2
{
get { return field2; } // Never null
}
}
方法调用
Guard子句也可用于确保方法参数不为null,因此您不会得到令人讨厌的NullRefereneceExceptions。
public void DoSomething(string param1, Type param2)
{
if (param1 == null)
throw new ArgumentNullException(nameof(param1));
if (param2 == null)
throw new ArgumentNullException(nameof(param2));
// param1 and param2 can now be used safely because they cannot be null
}
使用保护条款并不总是切实可行,但你应该在任何地方都使用null
对程序无用,以防止必须在任何地方添加null
检查逻辑。
如果null
对程序有意义(如Dave也提到的那样),则需要明确检查以防止它。
if (variable != null && variable.Field != null)
{
// Do something with variable.Field
var foo = variable.Field;
}
要么
var foo = variable?.Field;
空对象模式
最后,如果一个程序需要引用null
比明确检查各地更好的方法是制作一个具体的对象来表示null
而不是实际使用null
对象指针。这被称为Null Object Pattern。
public interface IService
{
void DoSomething();
}
public class NullService : IService
{
public void DoSomething()
{
// Do nothing at all to represent a no-op
}
}
public class MyService : IService
{
public void DoSomething()
{
// Do something interesting
Console.WriteLine("Something was done.");
}
}
然后,在程序中,您可以使用MyService
实例来表示实际值,或者使用NullService
来表示null
。
IService service1 = new MyService();
IService service2 = new NullService();
service1.DoSomething();
service2.DoSomething(); // Doesn't throw NullReferenceException
以上是关于如何覆盖NullReferenceException行为以获得更好的信息性错误?的主要内容,如果未能解决你的问题,请参考以下文章