命名参数的规则是什么,为什么
本文关键字:是什么 为什么 规则 参数 | 更新日期: 2023-09-27 18:28:28
考虑这样的方法
void RegisterUser(string firstname, string lastname, int age);
当我调用它们时,我喜欢显式命名这样的方法的参数,因为有人很容易混淆firstname
和lastname
参数。 但是,对于age
来说,这并不是真正必要的。 例如,我认为从清晰度的角度来看,这应该是可以的。
RegisterUser(firstname: "John", lastname: "Smith", 25);
但是会抛出以下错误:
指定所有固定参数后,必须显示命名参数规范
另一个有趣的事情是,如果签名是
void RegisterUser(int age, string firstname, string lastname);
然后按如下方式调用它不会抛出错误
RegisterUser(25, firstname: "John", lastname: "Smith");
为什么 C# 设计成这样? 如果允许第一种方案,编译器是否会变得复杂?
也许能够弄清楚,但对于我们人类来说,几乎不可能知道 25 是指第 1 个还是第 3 个参数。特别是因为它开辟了混合参数的可能性。为什么不呢
MyFunction(firstname: "josh", 25, "smith", someotherargument: 42)
你会如何解释这一点,年龄为25岁,姓氏为史密斯? 为它制定规则,编译器可以实现它。但是对人类来说有什么意义。代码混淆不应该那么容易
一种语言应该使犯错误变得困难,而不是更容易
注意:如果较早的参数稍后命名,则排序开始发生奇怪的事情。(就像我例子中的名字和史密斯一样(,因为这样就变成了一个难题,让你的未命名参数映射到正确的参数。这是可以做到的,但代码不应该产生谜题
这是因为当您命名参数时,编译器会根据名称映射它,只要所有必需的参数都存在,编译器就会忽略函数定义。在您的情况下,它不知道 25 是什么。对我们来说,这似乎是合乎逻辑的,它必须是年龄,但如果你把你的例子改为:
void RegisterUser(string firstname, string lastname, int age = 0, int weight = 0);
然后说:
RegisterUser(firstname: "John", lastname: "Smith", 25);
然后编译器不知道如何处理最后 25 个。这种调用函数的方式主要用于具有大量默认值的函数,您只想设置一些默认值。
当不命名你的参数时,你基本上是在说你严格遵循函数定义设置的结构。
预期用途,包括:
void M(int a = -1, int b = -1, int c = -1, int d = -1, int e = -1);
例如,您可以通过位置表示法指定这些可选参数的子集:
M(42, 28, 101); // gives a, b, and c in order; omits d and e
或者可以使用命名参数:
M(d: 50, a: 42, c: 101); // gives three arguments in arbitrary order
或者你可以组合它们,从位置参数开始,然后切换到命名参数:
M(42, 28, e: 65537, d: 50); // mixed notation OK
您遇到限制的原因是:
M(c: 101, 7, 9, b: 28, 666); // ILLEGAL!
会令人困惑。
我可以看到您建议在特定调用中保留位置顺序,然后为了清楚起见,仅包含一些参数的名称。但是,这种使用似乎不是语言设计者的优先事项。
我建议您在使用命名样式的原因是清晰时命名所有参数(而不是只需要指定参数的适当子集(。
所有命名参数都必须在位置参数之后;您不能在样式之间切换。 位置参数始终引用方法声明中的相应参数。 不能通过稍后使用命名参数指定参数来使位置参数跳过参数。 编译器使用临时局部变量。 然后它对参数槽中的那些局部变量重新排序,我的猜测是编译器按顺序绑定参数,直到找到命名参数,然后它丢弃它已经绑定的没有名称的参数,并在编译器使用临时局部变量时重新排序。 按名称绑定其余部分,例如,它将 25 与年龄绑定,然后重新排序名字:"John",姓氏:"Smith">
位置参数放在命名参数之前。位置参数始终引用方法声明中的相应参数。
假设我的方法如下:
void Dimensions(int height, int breadth , int length);
我叫它像
Dimensions(3, length: 12, 24);
在这种情况下:
"3"是第一个参数,指的是高度,但"24"是第三个参数,指的是长度,但我们已经指定了长度值。
因此,也许为了克服这个障碍,默认情况下,c# 样式是在开始时放置位置参数以提供正确的引用。
此外,如果我们定义可选参数,则在末尾提供位置参数可能会导致错误的结果。
我认为在这种情况下的语言设计驱动任何函数的第一个命名参数。
使用您的示例
void RegisterUser(int age, string firstname, string lastname);
并称它
RegisterUser(25, firstname: "John", lastname: "Smith");
在遇到第一个参数之前,编译器根本不知道此函数是否将使用任何命名参数。因此,编译器执行顺序映射是一个安全的假设。
25:
编译器获取第一个参数,如果它是未命名的参数,那么它将立即与函数定义中的第一个参数映射。
名:
一旦遇到它的第一个命名参数,事情就会发生变化,因为编译器现在必须检查函数定义中提到的所有剩余参数以映射当前命名参数。
成功映射它的第一个命名参数后,顺序映射的轨迹已被破坏。因此,现在向编译器提供非命名参数是不可行的,因为现在它无法确定在哪里映射它。
如果您认为它应该记住最后一个非命名参数并从那里开始顺序映射,那么这也容易出错,因为刚刚定义的命名参数也可能是顺序正确的。
姓氏:
对于命名的论点来说,这是轻而易举的。
希望这对:)有所帮助