考虑以下遗留代码:
public class Foo
{
public void Blah(string frob)
{
if (frob == null)
throw new ArgumentException("frob");
}
public string nameof()
{
//boring code
}
}
现在想象一下这段代码的一部分正在被重构,我们希望以下列方式重构Blah
:
public void Blah(string frob)
{
if (frob == null)
throw new ArgumentException(nameof(frob)); //nameof contextual keyword
}
此解析器不会选择上下文关键字nameof
并将解析为私有方法nameof
,从而产生编译时错误(没有现有的重载)。为什么?如果方法是nameof(object o)
,我会理解这一点。
有没有我可以让编译器解析为内置的nameof
?我是否需要重构重命名方法的代码?
上面的示例是一个非常简化的场景,实际上nameof
方法在反射中被广泛使用,并且需要花费时间和精力来确保重命名已在所有地方正确完成。
答案 0 :(得分:8)
此解析器不会选择上下文关键字nameof,并将解析为提供编译时错误(没有现有重载)的私有方法名称。为什么呢?
"为什么"问题含糊不清;我会假设这个问题意味着"在设计这样的功能时会考虑哪些设计因素?"
创建新的上下文关键字时的高阶考虑是确保在新编译器中编译时没有现有程序更改的含义。例如,当我编写代码以在C#3中添加var
时,我们小心翼翼地确保
class var { public implicit operator var(int x) { ... } }
...
var y = 3;
将var
类型分配给y
,而不是int
。尽管这样的程序不太可能,但我们想打破根本没人。
同样,yield return
的语法是yield return
而不仅仅是yield
,因为*没有程序中有yield return
。但很多程序可能会说yield(123);
同样地,await
运算符仅在async
方法中有意义,并且from
和select
等在查询理解中有意义。
因此nameof
必须表示名为nameof
的标识符(如果一个在范围内),而不是运算符。请注意,C#不会尝试回溯。它没有说"嗯,它的范围,但是这会导致重载分辨率错误,所以我要回溯并假设它是关键字......"不,不,这是可怕的推理。 标识符nameof
在范围内,因此这几乎肯定是C#6之前的程序。 99.9%的可能性是该程序中每次使用nameof
打算引用标识符。
C#的基本设计原则是"如果看起来很奇怪,告诉开发人员他们知道问题。"您现在已经完全意识到程序中存在严重问题,现在您可以尝试修复它。 C#不是为了找到编译程序的任何可能的解释,无论多么奇怪和不可能。
答案 1 :(得分:0)
我想反射代码在类中搜索nameof
的某些内容可能与某个序列化框架类似...
在这种情况下,最好的临时解决方案可能是更新该代码以首先搜索函数NameOf
,如果该函数不存在,则搜索旧版nameof
。
这样,您就可以一次找到一个类。
如果您想立即执行完整修复并假设您的自定义nameof
函数从不接受任何参数,那么最佳解决方案可能是将所有nameof
重命名为NameOf
(或您喜欢的任何其他名称),然后在应使用nameof
时修复编译错误。如果您有很多代码并且刚刚开始使用nameof
运算符,这将是有意义的。
在任何情况下,不遵循.NET命名约定是一个坏主意,特别是对于可能在某些时候可能成为关键字的短名称。
也许让代码审查也是一个好主意,以便编写错误代码的人在离开公司之前修复它。
答案 2 :(得分:-1)
无论您是否可以强制代码使用nameof关键字而不是内置函数,您实际上不应该使用关键字作为方法名称。重命名方法名称。