c # метод с неограниченным количеством параметров или метод с массивом или списком?

21

Недавно я узнал, что вы можете создать какой-то метод с неограниченными параметрами, например:

SomeMethod(params int[] numbers);

но мой вопрос: в чем разница между этим и просто созданием метода, который получает список или массив?

SomeMethod(int[] numbers);
SomeMethod(List<int> numbers);

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

Быстрый поиск в Google не помог, надеюсь, вы могли бы мне помочь.

Рикардо Санчес Сантос
источник
посмотрите на это: stackoverflow.com/questions/434761/…
Mr.AF
В дополнение к моему ответу ниже paramsтакже требуется, чтобы тип аргумента был массивом. Если вам нужно использовать коллекцию, отличную от массива, возможно, имеет смысл paramsвместо этого предоставить не аргумент.
digitlworld
1
@digitlworld: Если эта тема вас интересует, см. обсуждение на github.com/dotnet/csharplang/issues/179 .
Эрик Липперт
Посмотрите на подпись и / или реализацию Console.Write.
3Dave

Ответы:

27

в чем разница между этим и просто созданием метода, который получает список или массив?

Разница между

void M(params int[] x)

а также

void N(int[] x)

это то, что М можно назвать так:

M(1, 2, 3)

или вот так:

M(new int[] { 1, 2, 3 });

но N можно назвать только вторым способом, а не первым .

возможно, это как-то влияет на производительность?

Влияние на производительность заключается в том, что независимо от того, вызываете ли вы Mпервым или вторым способом, вы получаете созданный массив. Создание массива влияет на производительность, поскольку требует времени и памяти. Помните, что влияние на производительность следует измерять относительно целей производительности; маловероятно, что стоимость создания дополнительного массива является определяющим фактором, определяющим разницу между успехом и провалом на рынке.

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

Для автора кода, вызывающего метод, это полностью и полностью удобно; это просто короче и легче писать

M(1, 2, 3);

вместо того чтобы писать

M(new int[] { 1, 2, 3 });

Это просто экономит несколько нажатий клавиш на стороне звонящего. Это все.

Несколько вопросов, которые вы не задавали, но, возможно, хотели бы знать ответ на:

Как называется эта функция?

Методы, позволяющие передавать переменное число аргументов на стороне вызывающей стороны, называются переменными . Методы Params - это то, как C # реализует переменные методы.

Как работает разрешение перегрузки с помощью метода вариаций?

Столкнувшись с проблемой разрешения перегрузки, C # будет рассматривать как «нормальную», так и «расширенную» формы, и «нормальная» форма всегда выигрывает, если применимы обе. Например, рассмотрим это:

void P(params object[] x){}

и у нас есть звонок

P(null);

Есть две применимые возможности. В «нормальной» форме мы вызываем Pи передаем нулевую ссылку для массива. В «развернутом» виде мы называем P(new object[] { null }). В этом случае нормальная форма побеждает. Если у нас был вызов, P(null, null)то обычная форма неприменима и расширенная форма выигрывает по умолчанию.

Задача : предположим, у нас есть var s = new[] { "hello" };и звонок P(s);. Опишите, что происходит на сайте вызова и почему. Вы можете быть удивлены!

Задача : предположим, у нас есть и то void P(object x){}и другое void P(params object[] x){}. Что делает P(null)и почему?

Задача : предположим, у нас есть и то void M(string x){}и другое void M(params string[] x){}. Что делает M(null)и почему? Чем это отличается от предыдущего случая?

Эрик Липперт
источник
Ch2: P(null)против P((object)null)против P((object[])null)- где я могу найти объяснение этой разницы? Такое ощущение, что у nullнего есть какой-то особый тип , который преобразует в массив, а не в object ( P(null)), но находит преобразование в строку или массив строк неоднозначным ( M(null)) ... что выглядит очень странно, ожидал бы, что он будет неоднозначным в обоих случаях или выберет версия с одним аргументом (чего нет). Но я полагаю, это больше связано с тем, чтобы paramsбыть как-то универсальным , как универсальная ссылка ( &&) в шаблонах C ++, и, следовательно, лучше соответствовать (в Ch2, а не в Ch3).
Фирда
@firda: Вы можете найти объяснение в спецификации C #. Причина странности заключается в следующем: ноль может быть преобразован в объект, строку, объект [] и строку []. Поэтому возникает вопрос о разрешении перегрузки: какое преобразование лучше ? Управляющее правило в этом случае является конкретным, а не общим . Как мы можем сказать, какой тип является более конкретным? Правило таково: все жирафы - животные, но не все животные - жирафы, поэтому жираф более специфичен, чем животное. Все object[]есть object, но не все objectесть object[], поэтому object[]конкретнее.
Эрик Липперт
@firda: Теперь вы знаете, почему строки неоднозначны. Это НЕ правда, что «все string[]есть string». На самом деле НЕТ string[]есть stringи НЕТ stringесть string[], поэтому stringне является более конкретным или более общим string[], и мы получаем ошибку двусмысленности при запросе выбора.
Эрик Липперт
Все object[]они object... object[]более конкретны, поэтому P(null)речь идет о более конкретном массиве, thx, это то, чего мне не хватало. Здесь можно найти некоторые правила: docs.microsoft.com/en-us/dotnet/csharp/language-reference/…
firda
5

Просто сделал маленький прототип. Кажется, ответ таков: paramsэто просто синтаксический сахар для передачи в массив. Это не совсем сюрприз. Я создал две версии одного и того же метода, где единственным отличием является ключевое слово «params». IL, сгенерированный для обоих, был идентичен, за исключением того, что System.ParamArrayAttributeк paramsверсии был применен a .

Кроме того, IL, сгенерированный на сайте вызова, также был тем же, что и я, вызывающий метод с объявленным вручную, new int[]и вызывающий метод, используя только paramsаргументы.

Таким образом, ответ, кажется, «удобство». Там, кажется, нет никакой разницы в производительности. paramsВместо этого вы также можете вызвать функцию с массивом, так что это тоже не удивительно. Это сводится к тому, что потребителю вашего метода проще вызывать его с любым количеством параметров (например someMethod(1, 2, 3)), чем всегда сначала создавать коллекцию (например someMethod(new List<int>() { 1, 2, 3 } )).

digitlworld
источник
4

Функция неограниченных параметров предлагает следующие преимущества во многих сценариях:

  1. Слабая связь
  2. Улучшенное повторное использование
  3. Лучшая общая производительность приложения

Вот пример, где опция неограниченных параметров - отличный выбор

Учтите, что приложение для отправки электронной почты должно быть построено.

Функция, отправляющая электронное письмо, должна иметь возможность обрабатывать одно или несколько значений для полей «Кому», «CC» и «BCC».

Если типы параметров фиксируются как массивы или списки для всех полей (To, CC, BCC), то вызывающая функция будет вынуждена иметь дело со всей сложностью определения 3-х массивов или списков для вызова функции отправителя электронной почты. ,

Даже если вызывающая сторона хочет отправить электронное письмо только на один адрес, функция отправителя электронной почты заставит вызывающую функцию определить и отправить 3 различных массива в качестве параметров.

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

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

Gopinath
источник
2

Из неисполнения , стиль точки зрения, paramsключевое слово действительно приятно иметь , когда вы хотите отправить дополнительный список параметров.

Лично я бы использовал, paramsкогда мой код был что-то вроде

SomeMethod('Option1', 'Option17');

void SomeMethod(params string[] options)
{
    foreach(var option in options)
    {
        switch(option): ...
    }
}

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

Я бы использовал, arrayили listкогда я знаю, что я всегда буду передавать эту функцию набор данных, которые уже собраны как

List<User> users = GetUsersFromDB();
SomeMethod(users);

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

OliveJuice
источник
1

Соглашение о вызовах отличается. Например ...

public class Test
{
    public void Method1( params int[] nums )
    {
        nums.ToList().ForEach( n => Console.WriteLine(n) );
    }

    public void Method2( List<int> nums )
    {
        nums.ForEach( n  => Console.WriteLine(n) );
    }   
}

void Main()
{   
    var t = new Test();
    t.Method1( 1, 2, 3, 4 );
    t.Method2( new List<int>() { 1, 2, 3, 4 } );
}

В первом случае вы можете передать в метод столько целых чисел, сколько отдельных параметров. Во втором случае вам нужно создать экземпляр списка и передать его.

JP Alioto
источник