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/isissue/179 .
Ерік Ліпперт

Подивіться на підпис та / або реалізацію Console.Write.
3Dave

Відповіді:


27

в чому різниця між цим і просто створенням методу, який отримує список або масив?

Різниця між

void M(params int[] x)

і

void N(int[] x)

полягає в тому, що M може називатися так:

M(1, 2, 3)

або так:

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

але N може називатися лише другим , не першим способом.

можливо, це має певний вплив на продуктивність?

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

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

Це суто і цілком зручність для автора коду, який викликає метод; просто коротше і простіше писати

M(1, 2, 3);

замість того, щоб писати

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

Це просто економить кілька натискань клавіш на стороні абонента. Це все.

Кілька питань, які ви не задавали, але, можливо, хотіли б знати відповідь:

Як називається ця особливість?

Методи, що дозволяють передавати змінну кількість аргументів на стороні виклику, називаються варіатичними . Параметри методів - це те, як 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)vs. P((object)null)vs. P((object[])null)- де я можу знайти пояснення цієї різниці? Він відчуває , як nullбув якийсь - то особливий тип , який перетворює в масив , а не об'єкта ( P(null)), але знайти перетворення в рядок або масив рядків неоднозначних ( M(null)) ... , який відчуває себе дуже дивно, і слід було очікувати , що або неоднозначності в обох випадках або вибрати версія одного аргументу (чого немає). Але я думаю, це більше про paramsте , щоб бути якось загальним , як універсальне посилання ( &&) у шаблонах C ++, і, таким чином, краще відповідати (у Ch2, а не в Ch3).
firda

@firda: Ви можете знайти пояснення в специфікації C #. Причиною дивацтва є: null може бути конвертованим в об'єкт, string, object [] і string []. Тож питання, яке постає перед роздільною здатністю перевантаження: таке, яке перетворення найкраще ? Правило контролю в цьому випадку є конкретним, краще, ніж загальне . Як ми можемо сказати, який тип є більш специфічним? Правило таке: всі жирафи - це тварини, але не всі тварини - жирафи, отже, жирафа є більш специфічною, ніж тварина. Усі 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- це просто синтаксичний цукор для проходження в масиві. Це насправді не сюрприз. Я створив дві версії того ж методу, де єдина різниця - ключове слово «парами». ІЛ, генерований для обох, був ідентичним, за винятком того, що System.ParamArrayAttributeдо paramsверсії застосовано a .

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

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


4

Функція необмежених параметрів пропонує наступні переваги у багатьох сценаріях:

  1. Вільна муфта
  2. Підвищена повторна використання
  3. Краща загальна продуктивність програми

Ось приклад, коли варіант з необмеженими параметрами є чудовим вибором

Врахуйте, що потрібно створити додаток для надсилання електронних листів.

Функція, що надсилає електронний лист, повинна вміти обробляти або окремі, або декілька значень для полів "До", "СС" та "BCC".

Якщо типи параметрів закріплені за масивами або списками для всіх полів (To, CC, BCC), то виклична функція буде змушена вирішити всю складність визначення 3 масивів або списків, щоб викликати функцію відправника електронної пошти. .

Навіть якщо абонент хоче надіслати електронний лист лише на одну адресу, функція відправника електронної пошти змусить абонента визначити та надіслати 3 різні масиви як параметри.

Якщо функція відправника електронної пошти використовує необмежений параметр, тоді функція абонента не повинна мати справу з усією складністю.

Підхід з необмеженими параметрами сприяє кращому виконанню програми за допомогою програми, уникаючи створення масивів або списків, де це не потрібно.


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в гнучкості, яку вона додає. Це може мати відносно незначний вплив на ваш код, але це все-таки хороший інструмент.


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 } );
}

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.