在C#代码中是否存在位置参数不足的情况? 我真的没有看到命名参数的任何好处,相反我可以看到过度使用命名参数会使代码难以阅读?所以我的问题是,为什么会有人使用它们,如何帮助编写更好的代码,因为我确信它们没有理由没有实现?
对我来说这看起来更干净:
private void Foo(1, true);
然后:
private void Foo(bar: 1, baz: true);
答案 0 :(得分:37)
命名参数旨在提高可读性。例如,我刚刚使用了一个
public void MarkAsDone(bool skipped) {}
现在通过调用没有名称的方法我们有一个歧义
MarkAsDone(true); //does true mean that it is successfully done?
可以通过使用名称
进行澄清来解决MarkAsDone(skipped: true);
我认为使用named参数会使客户端代码变得不那么模糊。
当有多个具有相同类型的可选参数时,它们可用于唯一地标识可选参数
MarkAsDone(int first, int second=0, int third=0) {}
///
MarkAsDone(1, third: 3);
答案 1 :(得分:17)
我使用命名参数使呼叫站点更清晰,当我有默认值的参数时。已经在许多不同的答案中讨论了默认值的情况,所以我们来谈谈呼叫站点的清晰度。
使用metasyntactic变量进行分析并不会突出其有用性。考虑一下,如果你愿意的话,这更像是“现实世界”。
让我们看一下呼叫网站:
something.OpenFile(documentPath, true);
这是怎么回事?它将打开documentPath
。还有别的吗?还有什么?我记不清了,即使我一周前写过OpenFile
。
以下是OpenFile
的三个不同的相对现实的例子。
void OpenFile(string path, bool shouldOverwrite)
void OpenFile(string path, bool preserveExisting)
void OpenFile(string path, bool enableWriting)
使用命名参数,我们可以使呼叫站点清晰:
something.OpenFile(documentPath, shouldOverwrite: false);
很明显,文件不会被覆盖。
something.OpenFile(documentPath, preserveExisting: false);
很明显,如果需要,文件将被覆盖。
最后,我们有:
something.OpenFile(documentPath, enableWriting: false)
很明显,该文件只能打开阅读。
这个特定的例子可以用枚举等其他东西来解决吗?是。你能不能改变代码?没有。其他人对bool参数有同样的持久仇恨吗?不。: - )
你可以用命名参数来做吗?是。好的本地变量名有用吗?极大。
答案 2 :(得分:12)
当我们需要使用这样的方法时,我们发现了一个非常有趣的命名参数:
private void ShowPopup(FrameworkElement content,
string title = "My Application",
bool isDialog = true,
double? width = null,
double? height = null,
double? offsetX = null,
double? offsetY = null,
bool isTransparent = false,
... etc)
其中几乎所有参数都是可选的。在某些情况下,您需要将所有这些参数保留为默认值,除了一个或几个参数,例如:
PopupHelper.ShowPopup(_view, isTransparent: true);
或类似的东西。
答案 3 :(得分:7)
当使用可选参数调用方法时,它们很有用 - 实际上是隐式需要的 - 因为当您使用可选参数调用方法时,您必须指定要传递的方法,否则您必须提供整个列表到最后一个一个你想要使用的。
给出这样的方法:
public void Do(Thing thing, bool doItTwice = false, bool logResults = false,
string something = null, bool makeTeaAfterwards = false)
然后必须使用命名参数以避免必须指定整个列表:
Do(thing, makeTeaAfterwards: true);
而不是:
Do(thing, false, false, null, true);
后者的缺点是必须复制默认值,这会引入错误。
答案 4 :(得分:4)
我不确定,但我认为您误解了命名参数。
请参阅: http://www.dotnetperls.com/named-parameters
基本上,当您将大量参数发送到方法时,它们非常有用。使用命名参数,您可以确定要将哪些参数发送到方法
Method1(test1: 1, ..., test20: 10);
你应该小心使用它,因为它有很大的性能缺点。
答案 5 :(得分:3)
如果你有一个方法签名,如:
private void Foo(int bar, bool baz);
然后命名的参数没有多大帮助,没有。
但想象一下方法签名如:
private void Foo(bool bar, int baz=0, int qux=0);
并且说你想传递默认值baz
但是qux
的参数,然后命名参数帮助那里:
Foo(true, qux:1);
答案 6 :(得分:2)
这些天C#支持可选参数,例如:
public void Dance(string song = "makarena",
string location = "your house",
string performer = "Michael",
DateTime? date = null,
int milliseconds = 0,
Action callback = null)
{
///party code
}
现在你可以跳过一些参数(按任意顺序)来调用它:
Dance(location : "my house", date : DateTime.Now, performer : "Christina");
我测试了代码。可悲的是,我没有看到克里斯蒂娜的性感舞蹈,因为我忘了设置毫秒参数:P(不是我的错,但是那些做API的人,他们为什么要选择毫秒?:P)。
我的观点是,这在COM Interop和类似情况下的真正价值。例如,Office COM对象有一些方法,其中包含许多没有这些参数的参数(例如Word.Documents.Open)。
答案 7 :(得分:2)
回答了一个被删除的类似问题,所以我会在这里发布..我认为Com Call论证还没有被包括在内:
使用命名参数有一两个很好的理由。
1)使用Com调用/可选参数时
想象一下:
var excelApp = new Microsoft.Office.Interop.Excel.Application();
excelApp.Workbooks.Add();
excelApp.Visible = true;
var myFormat =
Microsoft.Office.Interop.Excel.XlRangeAutoFormat.xlRangeAutoFormatAccounting1;
excelApp.get_Range("A1", "B4").AutoFormat(myFormat, Type.Missing,
Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
在C#3.0及更早版本中,您需要提供参数 每个参数。
但是,您可以通过使用C#4.0中引入的命名参数和可选参数来大大简化对AutoFormat的调用。
excelApp.Range["A1", "B4"].AutoFormat( Format: myFormat );
2)命名参数可以创建更好/更清晰的代码
File.Copy("source.txt", "destination.txt", true);
除非您是熟悉此方法的开发人员,否则您只能猜测最后一个布尔参数的用途。使用C#4.0命名参数,可以像这样编写代码以更清楚地表达意图:
File.Copy("source.txt", "destination.txt", overwrite: true);
3)它是shoprtenes代码
改为使用
Person person = new Person();
person.FirstName = "John";
person.LastName = "Smith";
person.DateOfBirth = new DateTime(1970, 1, 1);
你可以使用(取决于可读性偏好)
Person person = new Person() { FirstName = "John", LastName="Smith", DateOfBirth = new DateTime(1970, 1, 1)};
答案 8 :(得分:1)
在方法的参数值绑定到数据库中的字段的应用程序中(例如,具有数据库属性的测试对象的构造函数),基于数据库的更改,顺序可能的参数可能会更改。如果方法参数的数据类型保持不变,并且手工编写的代码(例如单元测试)调用了生成的方法,那么很难看出手工编写的代码不再与生成的代码匹配。命名参数可以帮助防止这种情况。
例如:
数据库有一个表MyCreation
,其中有列IsBig
和IsColoured
。使用ORM,存在一种用于创建样本数据的测试方法来填充此类数据:
/* Generated Code from an ORM */
public IMyCreation CreateTestObject(bool isBig, bool isColoured)
{
IMyCreation _myCreation = new MyCreation();
_myCreation.IsBig = isBig;
_myCreation.IsColoured = isColoured;
return _myCreation;
}
手动测试类中的方法利用了这一点:
var myCreation = mcTest.CreateTestObject(false, true);
现在,如果要更改数据库,例如参数被重命名(IsBig
变成IsGrande
),然后参数的顺序可能会更改,并且生成的函数现在变为:
/* Generated Code from an ORM */
public IMyCreation CreateTestObject(bool isColoured, bool isGrande)
{
IMyCreation _myCreation = new MyCreation();
_myCreation.IsColoured = isColoured;
_myCreation.IsGrande = isGrande;
return _myCreation;
}
但是,所有内容仍然可以编译。调用代码仍然是有效,但不再正确,因为每个参数的值都不同。
var myCreation = mcTest.CreateTestObject(false, true);
如果使用命名参数,则可以防止生成的参数更改:
var myCreation = mcTest.CreateTestObject(isBig: false, isColoured: true);
...然后此代码将中断(在重命名参数的情况下)-这是所希望的!
var myCreation = mcTest.CreateTestObject(isBig: false, isColoured: true);
是正确的,无论函数签名是否为
public IMyCreation CreateTestObject(bool isBig, bool isColoured)
或
public IMyCreation CreateTestObject(bool isColoured, bool isBig)
对于生成的代码,丑陋的代码更容易容忍,an approach like this可能有助于强制使用命名参数。
答案 9 :(得分:0)
它们可以是一个很好的意图声明,并提高可读性,其中参数是相同的类型或可以隐式转换。
E.g
int Duration(d1,d2)
那是从d1到d2还是d2-d1? Intellisense可能会告诉您参数是否具有良好的名称,或者文档是否正确且是最新的。或者你可以查看代码......
使用多个可选参数它们甚至更有用,避免像所有可选参数一样的规则必须是最后一个,并且必须指定所有不想使用默认值的参数。如果由于某种原因你需要重新计算参数列表的因素,那么总的噩梦。 你可能想要考虑简洁和简洁之间的区别。简洁总是好的,很少简洁。
答案 10 :(得分:0)
我觉得它牺牲了紧凑性以便于阅读。假设您有一个清除一些不需要的元素的函数。不需要的可能是:旧的,已弃用的,未使用的:
timezone.now
当然,还有很多其他方法可以实现这一点,从bitwise标志到intellisense。但是,当用Python编程时,我使用命名的params非常分配。当然,那里没有强力打字,而且工具更糟糕。
答案 11 :(得分:0)
示例功能
public void TrackDataChange(IEntity oldData, IEntity newData)
旧电话:
dataTrackerService.TrackDataChange(newData: valuesFromClient.ToEntity(), oldData: valuesFromDb.ToEntity())
新电话:
var oldData = new ValueEntityConverter(valuesFromDb).Entity;
var newData = new ValueEntityConverter(valuesFromClient).Entity;
dataTrackerService.TrackDataChange(newData, oldData);
当然可以编译,但是现在得到的数据被弄乱了,因为
不确定您可以完全责怪任何一个开发人员...