命名参数何时有用?

时间:2013-12-15 23:02:08

标签: c#

在C#代码中是否存在位置参数不足的情况? 我真的没有看到命名参数的任何好处,相反我可以看到过度使用命名参数会使代码难以阅读?所以我的问题是,为什么会有人使用它们,如何帮助编写更好的代码,因为我确信它们没有理由没有实现?

对我来说这看起来更干净:

private void Foo(1, true);

然后:

private void Foo(bar: 1, baz: true);

12 个答案:

答案 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,其中有列IsBigIsColoured。使用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);

当然可以编译,但是现在得到的数据被弄乱了,因为

  • 最初顺序是错误的,但是由于名称的缘故,仍然可以正常工作
  • 有人删除了姓名,但没有检查订单

不确定您可以完全责怪任何一个开发人员...