考虑以下代码:
A a = null;
a.f(); //Will it throw NullReferenceException?
上面会抛出NullReferenceException
吗?
答案是:这取决于f()
是什么。
这种差异导致了一个问题:C#编译器如何实现和查看每种类型的方法?另外,为什么成员方法必须抛出异常,即使它不访问任何成员数据?似乎C#编译器提前假设 成员方法将访问成员数据,因此如果对象为null则抛出异常,因为无法访问使用null对象成员数据。但是,在扩展方法的情况下,它会推迟此决定,直到实际尝试使用空引用only then it throws exception来访问成员数据。
我的理解到底有多远?如果是这样,为什么会出现这种差异?
是的,我知道如果f()
是一种扩展方法,那么a.f()
相当于编写AExt.f(a)
,因此后者不应该抛出异常,直到a
为用来访问会员。但我的重点主要放在编译器实现上(它可以以相同的方式实现成员方法)。
答案 0 :(得分:2)
是的,这是此代码的行为方式(如果扩展方法不检查null
并自行抛出异常)。你是正确的,如果该方法不能访问该类的任何实例字段(直接或间接),则null
上的非虚拟实例方法可以正常工作。
但是the language designers felt this would be confusing,所以他们通过使用null
IL指令确保对象不是callvirt
。
使用扩展方法,这并不会令人困惑(没有人希望this
是null
,但应该检查声明this IEnumerable<TSource> source
的参数。并且您可以将扩展方法称为常规静态方法,因此无论如何都应该检查null
。此外,两种调用函数的方式应该完全相同。
答案 1 :(得分:1)
当对象为null时,实例方法必须抛出NullReferenceException
,因为它们本身保证在执行该方法时对象可用。将实例方法视为对象实例执行的操作以及当对象本身不存在时,甚至无法调用该操作。扩展方法只是语法糖,编译器会立即翻译它们,如上所述AExt.f(a)
。这只是一个便利性,可以帮助您更好地阅读代码。
对于您想要调用方法而不管实例是否可用的情况,该语言提供静态方法作为功能。如您所知,扩展方法是静态方法。