使用 C# 的实际原因 "named arguments"
Practical Reasons to use C# "named arguments"
我发现 C# 中的这个功能 "named arguments" 真的很奇怪,因为我从中看到了两个缺陷。书中说"named arguments give you the "能够以任何顺序传递参数。
我认为此 C# 功能存在两个缺陷:
这违反了计算机科学中的"information hiding"。
(即:使用该方法的最终用户需要知道参数名称和数据类型才能使用该功能。)来自 Java 背景,这很奇怪。为什么要将参数名称暴露给用户?
它很容易产生歧义,从而导致错误。
(程序员需要做额外的思考,当程序员结束编写使用相同方法名称(也称为重载方法)的方法时,问题可能会逐渐蔓延。你总是会得到一个 "call is ambiguous when you have two methods with same name with the same parameters for each other even if the other method has a extra parameters with different data types. The only fix I can think of is to make the data type a " 强制参数”,也就是没有参数的参数一个默认值,这样编译器就不会混淆。但是这个修复只是一个绷带解决方案,会导致另一个最坏的情况(见下文)
业内人士至今还使用这个概念吗?既然如此,为什么要打破这两条规则才能得到"ability to pass arguments in any order when calling the method"?
TLDR:通过引入可能的最坏情况(编译器选择了错误的方法调用......尽管这两种方法相似)来阐明我正在谈论的内容的示例:
namespace ConsoleApplication1
{
class Venusaur
{
static void Main(string[] args)
{
new Venusaur().optMethod(fourth: "s");
}
public void optMethod( string third , string fourth = "hello", int fifth = 23, string two = "w")
{
// what if I wanted this method to run instead of the method below me
Console.WriteLine("did not execute");
}
public void optMethod(string third = "Byte", string fourth = "hello", int fifth = 4)
{
// But this method ran instead
Console.WriteLine("run");
}
}
}
我发现你的第一个论点完全似是而非。不管你喜不喜欢,参数的名称是每个方法语义的一部分,特别是对于抽象classes 和将在子classes 中重写的方法。参数的名称是向方法用户发出关于该方法语义的关键信号,以及每个参数在该语义中的作用。此外,为了理解整个 class 层次结构中的语义,参数名称必须一致,并且不受每个级别的个别程序员的突发奇想的影响。
你的第二个论点我觉得完全无法理解。如果您在这里确实有一个有效的观点,我建议您重写它以更加清晰。
也就是说,我很少在我的方法调用中使用命名参数;例外情况是接受两个以上相同类型的连续参数的方法。在这种情况下,我喜欢给它们命名,这样我,作为代码的未来 reader,实际上可以破译方法的使用,而不必经常悬停以查看智能感知。
命名参数是另一项添加的技术,它使与 COM(和类似技术)的互操作性更容易。
取method to open a Word document using COM:
Document Open(
[In] ref object FileName,
[In, Optional] ref object ConfirmConversions,
[In, Optional] ref object ReadOnly,
[In, Optional] ref object AddToRecentFiles,
[In, Optional] ref object PasswordDocument,
[In, Optional] ref object PasswordTemplate,
[In, Optional] ref object Revert,
[In, Optional] ref object WritePasswordDocument,
[In, Optional] ref object WritePasswordTemplate,
[In, Optional] ref object Format,
[In, Optional] ref object Encoding,
[In, Optional] ref object Visible,
[In, Optional] ref object OpenAndRepair,
[In, Optional] ref object DocumentDirection,
[In, Optional] ref object NoEncodingDialog,
[In, Optional] ref object XMLTransform
);
它有不少于16个参数,其中15个是可选的。
如果您想指定 XMLTransform
参数但不关心其余参数怎么办?
您必须使用位置参数指定其余部分。使用命名参数,调用简单地变为:
doc.Open("somefilename.doc", XMLTransform: xxx);
与 dynamic
和其他一些东西一起,它并不是解决很多问题的好方法,但是当你有一个 API 严重依赖于有很多方法的方法时还有很多参数,其中大部分是可选的,命名参数很有意义。
是的,您可以乱序指定参数。这并不意味着它是个好主意或它解决了某人真正遇到的问题。
1) 我不明白 "information hiding" 在哪里;方法签名是您要求最终用户给您的。他显然需要知道数据类型和名称来消除参数为什么指代什么的歧义。而且他仍然不知道你要用你要求的那些参数做什么(也许什么都不知道)所以需要对他隐藏的东西仍然是隐藏的。
2) 这里的问题是两个重载的错误选择,这两个重载仅在参数数量上有所不同,再加上省略某些参数(由可选参数提供)的能力会让你失去 "benefit"区分它们(除非你给出所有参数)
已(部分)为互操作场景动态引入命名参数;例如对于办公自动化,在 C#3 中你需要添加一堆 Type.Missing
参数,然后在 C#4 中你可以只提供那些你 want/need.
关于 COM 互操作的观点是最重要的。
如果我想使用的方法签名中有一个布尔参数,但没有明显的变量可以使用,我个人倾向于使用它。
示例:
我觉得更具可读性
var isSuccessful = myObject.Method(isRecursive: true)
然后
var isSuccessful = myObject.Method(true);
最终你可以使用更详细的语法
var isRecursive = true;
var isSuccessful = myObject.Method(isRecursive);
避免命名参数。
像这样调用方法的明显缺点是,如果由于某种原因方法签名发生更改,您的代码将不再生成。
我发现 C# 中的这个功能 "named arguments" 真的很奇怪,因为我从中看到了两个缺陷。书中说"named arguments give you the "能够以任何顺序传递参数。
我认为此 C# 功能存在两个缺陷:
这违反了计算机科学中的"information hiding"。 (即:使用该方法的最终用户需要知道参数名称和数据类型才能使用该功能。)来自 Java 背景,这很奇怪。为什么要将参数名称暴露给用户?
它很容易产生歧义,从而导致错误。 (程序员需要做额外的思考,当程序员结束编写使用相同方法名称(也称为重载方法)的方法时,问题可能会逐渐蔓延。你总是会得到一个 "call is ambiguous when you have two methods with same name with the same parameters for each other even if the other method has a extra parameters with different data types. The only fix I can think of is to make the data type a " 强制参数”,也就是没有参数的参数一个默认值,这样编译器就不会混淆。但是这个修复只是一个绷带解决方案,会导致另一个最坏的情况(见下文)
业内人士至今还使用这个概念吗?既然如此,为什么要打破这两条规则才能得到"ability to pass arguments in any order when calling the method"?
TLDR:通过引入可能的最坏情况(编译器选择了错误的方法调用......尽管这两种方法相似)来阐明我正在谈论的内容的示例:
namespace ConsoleApplication1
{
class Venusaur
{
static void Main(string[] args)
{
new Venusaur().optMethod(fourth: "s");
}
public void optMethod( string third , string fourth = "hello", int fifth = 23, string two = "w")
{
// what if I wanted this method to run instead of the method below me
Console.WriteLine("did not execute");
}
public void optMethod(string third = "Byte", string fourth = "hello", int fifth = 4)
{
// But this method ran instead
Console.WriteLine("run");
}
}
}
我发现你的第一个论点完全似是而非。不管你喜不喜欢,参数的名称是每个方法语义的一部分,特别是对于抽象classes 和将在子classes 中重写的方法。参数的名称是向方法用户发出关于该方法语义的关键信号,以及每个参数在该语义中的作用。此外,为了理解整个 class 层次结构中的语义,参数名称必须一致,并且不受每个级别的个别程序员的突发奇想的影响。
你的第二个论点我觉得完全无法理解。如果您在这里确实有一个有效的观点,我建议您重写它以更加清晰。
也就是说,我很少在我的方法调用中使用命名参数;例外情况是接受两个以上相同类型的连续参数的方法。在这种情况下,我喜欢给它们命名,这样我,作为代码的未来 reader,实际上可以破译方法的使用,而不必经常悬停以查看智能感知。
命名参数是另一项添加的技术,它使与 COM(和类似技术)的互操作性更容易。
取method to open a Word document using COM:
Document Open(
[In] ref object FileName,
[In, Optional] ref object ConfirmConversions,
[In, Optional] ref object ReadOnly,
[In, Optional] ref object AddToRecentFiles,
[In, Optional] ref object PasswordDocument,
[In, Optional] ref object PasswordTemplate,
[In, Optional] ref object Revert,
[In, Optional] ref object WritePasswordDocument,
[In, Optional] ref object WritePasswordTemplate,
[In, Optional] ref object Format,
[In, Optional] ref object Encoding,
[In, Optional] ref object Visible,
[In, Optional] ref object OpenAndRepair,
[In, Optional] ref object DocumentDirection,
[In, Optional] ref object NoEncodingDialog,
[In, Optional] ref object XMLTransform
);
它有不少于16个参数,其中15个是可选的。
如果您想指定 XMLTransform
参数但不关心其余参数怎么办?
您必须使用位置参数指定其余部分。使用命名参数,调用简单地变为:
doc.Open("somefilename.doc", XMLTransform: xxx);
与 dynamic
和其他一些东西一起,它并不是解决很多问题的好方法,但是当你有一个 API 严重依赖于有很多方法的方法时还有很多参数,其中大部分是可选的,命名参数很有意义。
是的,您可以乱序指定参数。这并不意味着它是个好主意或它解决了某人真正遇到的问题。
1) 我不明白 "information hiding" 在哪里;方法签名是您要求最终用户给您的。他显然需要知道数据类型和名称来消除参数为什么指代什么的歧义。而且他仍然不知道你要用你要求的那些参数做什么(也许什么都不知道)所以需要对他隐藏的东西仍然是隐藏的。
2) 这里的问题是两个重载的错误选择,这两个重载仅在参数数量上有所不同,再加上省略某些参数(由可选参数提供)的能力会让你失去 "benefit"区分它们(除非你给出所有参数)
已(部分)为互操作场景动态引入命名参数;例如对于办公自动化,在 C#3 中你需要添加一堆 Type.Missing
参数,然后在 C#4 中你可以只提供那些你 want/need.
关于 COM 互操作的观点是最重要的。
如果我想使用的方法签名中有一个布尔参数,但没有明显的变量可以使用,我个人倾向于使用它。 示例:
我觉得更具可读性
var isSuccessful = myObject.Method(isRecursive: true)
然后
var isSuccessful = myObject.Method(true);
最终你可以使用更详细的语法
var isRecursive = true;
var isSuccessful = myObject.Method(isRecursive);
避免命名参数。
像这样调用方法的明显缺点是,如果由于某种原因方法签名发生更改,您的代码将不再生成。