Принудительное использование именованных аргументов в C#

В C# 4 появилась функция под названием именованные аргументы, которая особенно полезна в таких сценариях, как

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

Есть ли способ принудительно использовать именованные аргументы? Может быть, какой-то атрибут для применения к методу или переключателю компилятора, о котором я не знаю? Я думаю, это можно сделать с помощью инструментов инспектора кода, но я просто хочу знать, есть ли другой способ.

p.s.

Для тех, кому интересно, зачем это может понадобиться и почему бы просто не использовать класс/структуру для использования инициализаторы объектов бывают ситуации, когда это невозможно. Например, вызовы библиотек, не находящихся под вашим контролем, или странные соглашения по коду, которым вы должны подчиняться.


person UserControl    schedule 02.07.2012    source источник
comment
Я не вижу особой пользы в именованных аргументах, если только ваши аргументы не являются в основном необязательными.   -  person Bruno Brant    schedule 03.07.2012
comment
@BobKaufman Я бы предположил обратное. Когда вы компилируете код, он забывает, упоминался ли он как позиционный параметр, именованный или значение по умолчанию.   -  person Tim S.    schedule 03.07.2012
comment
Зачем вам это нужно? Вам навязывают кодовое соглашение?   -  person driis    schedule 03.07.2012
comment
@ТимС. Вы абсолютно правы. Согласен и отозван.   -  person Bob Kaufman    schedule 03.07.2012
comment
@driis, когда сигнатура метода содержит много аргументов одного типа, при ее вызове легко сделать ошибку.   -  person UserControl    schedule 03.07.2012
comment
Так что это соглашение, которое вы хотите обеспечить. Тогда я думаю, вам следует воспользоваться правилом FxCop.   -  person driis    schedule 03.07.2012
comment
@BrunoBrant Именованные аргументы делают код более самодокументируемым и, следовательно, более удобным в сопровождении. Например, сколько существует API, в которых вы должны передавать целые числа для строк и столбцов? И я уверен, что в 9 случаях из 10 люди должны пойти и проверить сигнатуру метода, чтобы убедиться, что аргументы находятся в правильном положении (как те, кто пишет, так и читают код). Именованные аргументы делают все это упражнение неуместным.   -  person MgSam    schedule 03.07.2012


Ответы (5)


Нет, не на языке C#. Он всегда будет принимать позиционные параметры, если все параметры предоставлены.

Вы можете создать собственное правило FxCop или правило StyleCop для обеспечения соблюдения этого - как указано в комментариях, скорее всего, правило StyleCop вас заинтересует. in (спасибо Крису).

person driis    schedule 02.07.2012
comment
Вы не можете создать пользовательское правило FxCop, потому что правила FxCop работают с двоичными файлами, а не с исходным кодом, и именованные аргументы компилируются. Я полагаю, вы могли бы создать собственное правило StyleCop. - person Kris Vandermotten; 03.07.2012
comment
@KrisVandermotten, я думаю, вы правы, однако FxCop может извлекать информацию из PDB, но я не знаю, входит ли использование именованных параметров в PDB. Если это не так, я думаю, вместо этого необходимо правило полицейского стиля. - person driis; 03.07.2012
comment
Нет, что касается именованных параметров, то это PDB: компьютер говорит нет!. - person Kris Vandermotten; 03.07.2012
comment
Извините за затык, я реализовал анализатор roslyn, чтобы принудительно использовать именованные аргументы для метод. Если решите попробовать - делайте это на свой страх и риск :) - person Nik; 17.08.2018

Можно заставить вызывающую сторону всегда использовать именованные аргументы. Я бы не стал этого делать в большинстве случаев, потому что это довольно уродливо, но это зависит от того, насколько сильно требуется безопасное использование метода.

Вот решение:

    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: "[email protected]");

При попытке использовать позиционные параметры код не будет компилироваться.

person user4698855    schedule 22.03.2015
comment
С вашей директивой препроцессора компилятора он действует так, как будто int _ = 0 даже не существует, если символ DEBUG не определен, поэтому он не имеет никакого эффекта для сборки выпуска... Я не думаю, что этот ответ делает то, что вы думаете, что это... - person Zack; 17.09.2015
comment
В результате неиспользуемый параметр _ со значением по умолчанию заставляет остальные следующие параметры иметь значения по умолчанию и называться при вызове. - person user4698855; 08.01.2016
comment
Некрасиво, но умно. - person joelsand; 28.10.2016
comment
фиктивный параметр также отображается в intellisense/R#, и это позор! - person GoatInTheMachine; 05.09.2017

Извините за бессовестную затычку!
Я реализовал анализатор Roslyn для обеспечения использование именованных аргументов для метода.

Поэтому, если вы установите анализатор RequireNamedArgs и добавите специальный комментарий перед методом, который должен вызываться с именованными аргументами:

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

Анализатор выдаст ошибку, если вызывающая сторона попытается использовать позиционные аргументы вместо именованных.

Посмотрите на это в действии: введите здесь описание изображения

Если решите попробовать - делайте это на свой страх и риск :)

person Nik    schedule 17.08.2018

Я также искал способ заставить именованные аргументы. Необязательные параметры могут быть опасны, особенно если у вас есть несколько параметров одного типа. Перегрузки почти всегда являются более безопасным решением, но бывают случаи, когда у вас есть метод, который может принимать множество комбинаций аргументов, поэтому создание 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" }); 

Таким образом, у вас есть только один необязательный параметр, и это для ваших необязательных параметров.

Редактировать: Возможно, я неправильно прочитал ОП. Вы не используете необязательные аргументы? Если нет, то этот ответ, вероятно, вам не поможет.

person oscilatingcretin    schedule 13.01.2016
comment
Согласованный. Сложная сигнатура метода — это запах кода, вместо этого таким методам следует передавать экземпляр контекста. Вероятно, это не единственный метод со сложными параметрами, вы увидите, что соседние методы в том же классе также имеют проблему, что делает объект контекста очень практичным. - person bboyle1234; 01.05.2018
comment
Проблема с объектом контекста (который, как я согласен, обычно является лучшим решением!) заключается в том, что обязательно инициализаторы объектов позволяют опустить инициализацию свойства; т. е. все аргументы фактически необязательны. - person Eamon Nerbonne; 04.02.2019

Я использую другой метод. В моей настройке у меня есть 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.");

    }

Надеюсь, вам понравится :)

person iJungleBoy    schedule 04.05.2016