43

C# 4 引入了一个名为命名参数的功能,该功能在以下场景中特别有用

int RegisterUser(string nameFirst, string nameLast, string nameMiddle, string email)

有没有办法强制使用命名参数?也许某些属性适用于我不知道的方法或编译器开关?我想这可以使用代码检查器工具来完成,但只是想知道是否有其他方法。

ps

对于那些对为什么需要它以及为什么不使用类/结构来利用对象初始化器感兴趣的人来说,有些场景是不可能的。就像调用不受您控制的库或您必须遵守的奇怪代码约定一样。

4

5 回答 5

32

可以强制调用者始终使用命名参数。在大多数情况下,我不会这样做,因为它相当丑陋,但这取决于需要多么安全的方法使用。

这是解决方案:

    int RegisterUser(
#if DEBUG
      int _ = 0,
#endif
      string nameFirst = null,
      string nameLast = null,
      string nameMiddle = null,
      string email = null) { /*...*/ }

第一个参数是一个不应该使用的虚拟参数(为了提高效率,它在 Release 中被编译掉了)。但是,它确保必须命名所有以下参数。

有效用法是命名参数的任意组合:

    RegisterUser();
    RegisterUser(nameFirst: "Joe");
    RegisterUser(nameFirst: "Joe", nameLast: "Smith");
    RegisterUser(email: "joe.smith@example.com");

尝试使用位置参数时,代码将无法编译。

于 2015-03-22T03:42:43.313 回答
28

不,不是 C# 语言。如果提供了所有参数,它将始终接受位置参数。

您可以构建自定义 FxCop 规则StyleCop 规则来强制执行此操作 - 正如评论中所指出的,您可能会感兴趣的 StyleCop 规则(感谢 Kris)。

于 2012-07-02T20:25:48.930 回答
6

对不起一个无耻的插件!
我实现了一个Roslyn 分析器来强制使用方法的命名参数。

因此,如果您安装 RequireNamedArgs 分析器并在应使用命名参数调用的方法之前添加特殊注释:

//[RequireNamedArgs]
int RegisterUser(string nameFirst, string nameLast, string nameMiddle, string email)

如果调用者尝试使用位置参数而不是命名,分析器将发出错误。

看看它的实际效果:在此处输入图像描述

如果您决定试一试——风险自负:)

于 2018-08-17T09:01:45.087 回答
4

我还寻求一种强制命名参数的方法。可选参数可能很危险,尤其是当您有多个相同类型的参数时。重载几乎总是一个更安全的解决方案,但有时你有一个方法可以接受许多参数组合,因此创建 20 个重载来考虑任何可能性是矫枉过正的。

在极端情况下,始终命名参数至关重要,我将创建一个没有定义构造函数的参数类。在你的情况下,你可以这样做:

public class UserRegistrationArguments
{
    public string nameFirst { get; set; }
    public string nameLast { get; set; }
    public string nameMiddle { get; set; }
    public string email { get; set; }
}

像这样称呼它:

RegisterUser(new UserRegistrationArguments { nameFirst = "Bob", nameLast = "Slob" });

您也可以像这样简化它:

public class UserRegistrationArguments
{
    public string nameMiddle { get; set; }
    public string email { get; set; }
}

int RegisterUser(string nameFirst, string nameLast, UserRegistrationArguments args = null)

...然后这样做:

RegisterUser("Bob", "Slob", new UserRegistrationArguments { nameMiddle = "Teh" }); 

这样,您只有一个可选参数,即您的可选参数。

编辑: 也许我没有正确阅读 OP。您没有使用可选参数吗?如果没有,那么这个答案可能对您没有帮助。

于 2016-01-13T23:01:16.250 回答
1

我正在使用另一种方法。在我的设置中,我有一个我一直期望的参数,然后是一堆可选的字符串,我真的想确保用户主动选择。所以我在这个列表中的第一个字符串是一个“陷阱”值,如果设置它,就会引发错误。像这样:

    public HtmlString Toolbar(DynamicEntity target = null, string dontRelyOnParameterOrder = Constants.RandomProtectionParameter, string actions = null, string contentType = null, object prefill = null)
    {
        if (!Enabled) return null;
        protectAgainstMissingParameterNames(dontRelyOnParameterOrder);

        var toolbar = new ItemToolbar(target, actions, contentType, prefill);

        return new HtmlString(toolbar.Toolbar);
    }

    private void protectAgainstMissingParameterNames(string criticalParameter)
    {
        if(criticalParameter != Constants.RandomProtectionParameter)
            throw new Exception("when using the toolbar command, please use named parameters - otherwise you are relying on the parameter order staying the same.");

    }

希望你喜欢 :)

于 2016-05-04T13:59:20.530 回答