C # 4.0 необов'язкові аргументи на вихід / перегляд


212

Чи дозволяє C # 4.0 додаткові outчи refаргументи?


2
Weell, C ++ ефективно має їх для параметрів "out" - у вас може бути аргумент адреси, ініційований як null, і це досить поширене записування коду бібліотеки, який заповнює таку структуру повернення, лише якщо покажчик не має значення null. Це ідіома, що повертається до використання null для "необов'язкових аргументів" в C API.
Енді Дент

53
@Ed і всі: чому це не має сенсу? Якщо функція "повертає" значення через "out", я не хочу, щоб його примушували приймати. Тепер я знаю, що з технічних причин компілятор все-таки повинен щось передати, але немає жодної причини, по якій він не міг просто створити локальну манекенку для мене за спиною.
Роман Старков

5
Можливо, це не має сенсу з точки зору того, як реалізуються речі чи що є необов'язковим параметром насправді. Але, як казали romkyns, було б дуже приємно мати "необов'язкові аргументи" - проаналізуйте це англійською мовою, а не CLR, і це стане розумним та бажаним в IMO.
Адам Толлі

9
C # ні, але VB.NET робить.
Джейсон

5
Це було побито до смерті, проте я не можу не згадати мою підтримку необов'язкових аргументів. Я досить звик до необов'язкових аргументів шляхом посилання шляхом встановлення за nullзамовчуванням ( я родом з PHP ) та тестування на те, nullщоб продовжувати заповнення аргументу ( для тих, хто знайомий, думаюpreg_match() ). У будь-якому випадку, поки я розумію з технічної точки зору, це може бути неможливо, і що PHP і C # є досить непорівнянними, все одно це буде " приємний " інструмент, щоб він був доступний.
Ден Лугг

Відповіді:


93

Як вже було сказано, це просто не дозволено, і я думаю, що це має дуже хороший сенс. Однак, щоб додати ще кілька деталей, ось цитата зі специфікації C # 4.0 , розділ 21.1:

Формальні параметри конструкторів, методів, індексаторів та типів делегатів можуть бути оголошені необов'язковими:

fix-parameter:
    атрибути opt -модифікатор параметра opt ідентифікатор типу opt- default-аргумент opt
default-argument:
    = вираз

  • Фіксований параметр з по замовчуванням-аргументом є необов'язковим параметром , в той час як з фіксованим параметром без по замовчуванням-аргумент є обов'язковим параметром .
  • Необхідний параметр не може з'являтися після необов'язкового параметра у списку формальних параметрів .
  • refАбо outпараметр не може бути по замовчуванням-аргумент .

Крім того, ви можете створити перевантаження з параметром ref / out. Так, у вас буде два визначення функції, але це здійснить те, що ви хочете
Чад

201

Немає.

Вирішення проблеми полягає в перевантаженні іншим методом, який не має параметрів out / ref і який просто викликає ваш поточний метод.

public bool SomeMethod(out string input)
{
    ...
}

// new overload
public bool SomeMethod()
{
    string temp;
    return SomeMethod(out temp);
}

Оновлення: Якщо у вас є C # 7.0 , ви можете спростити:

// new overload
public bool SomeMethod()
{
    return SomeMethod(out _);    // declare out as an inline discard variable
}

(Дякую @Oskar / @Reiner за вказівку на це.)


6
будь-яка ідея для більш елегантного рішення, ніж декларування темп / пустушки?
Луї Ріс

20
Що в цьому не елегантно? Мені виглядає ідеально пристойно.
Нейтрино

27
Можливо пристойний, але елегантний, безумовно, в іншій лізі. Те, що немає кращого рішення, не означає, що це є найсучаснішим указом.
o0 '.

7
Тримайтеся @ o0 '. В C # 7.0 ви зможете зробити це: return SomeMethod(out string temp). Дивіться більше тут: blogs.msdn.microsoft.com/dotnet/2016/08/24/…
Оскар

7
У C # 7.0 ви можете використовувати тимчасову змінну лише для запису на зразок:return SomeMethod(out _);
Reiner

64

Ні, але іншою чудовою альтернативою є те, що метод використовує загальний клас шаблонів для необов'язкових параметрів:

public class OptionalOut<Type>
{
    public Type Result { get; set; }
}

Тоді ви можете використовувати його наступним чином:

public string foo(string value, OptionalOut<int> outResult = null)
{
    // .. do something

    if (outResult != null) {
        outResult.Result = 100;
    }

    return value;
}

public void bar ()
{
    string str = "bar";

    string result;
    OptionalOut<int> optional = new OptionalOut<int> ();

    // example: call without the optional out parameter
    result = foo (str);
    Console.WriteLine ("Output was {0} with no optional value used", result);

    // example: call it with optional parameter
    result = foo (str, optional);
    Console.WriteLine ("Output was {0} with optional value of {1}", result, optional.Result);

    // example: call it with named optional parameter
    foo (str, outResult: optional);
    Console.WriteLine ("Output was {0} with optional value of {1}", result, optional.Result);
}

19
Це дуже розумне рішення, але слід пам’ятати одне, що компілятор не буде виконувати вимогу про призначення параметра для виходу до виходу з методу.
Кен Сміт

2
Мені це подобається, але якщо ви не хочете створювати новий клас, ви можете імітувати його, передаючи масив одного елемента.
zumalifeguard

30

Насправді є спосіб зробити це, що дозволено C #. Це повертається до C ++ і швидше порушує приємну об'єктно-орієнтовану структуру C #.

ВИКОРИСТОВУЙТЕ ЦІЙ МЕТОД З ОБЕРЕЖНОЮ!

Ось спосіб оголосити та записати свою функцію за допомогою додаткового параметра:

unsafe public void OptionalOutParameter(int* pOutParam = null)
{
    int lInteger = 5;
    // If the parameter is NULL, the caller doesn't care about this value.
    if (pOutParam != null) 
    { 
        // If it isn't null, the caller has provided the address of an integer.
        *pOutParam = lInteger; // Dereference the pointer and assign the return value.
    }
}

Потім викличте функцію так:

unsafe { OptionalOutParameter(); } // does nothing
int MyInteger = 0;
unsafe { OptionalOutParameter(&MyInteger); } // pass in the address of MyInteger.

Для того, щоб це скомпілювати, вам потрібно включити небезпечний код у параметрах проекту. Це дійсно хакі-рішення, яке зазвичай не слід використовувати, але якщо вам для якогось дивного, таємничого, загадкового, натхненного управлінням рішення дійсно потрібен необов'язковий параметр у C #, то це дозволить зробити саме це.


6

ICYMI: Включено до перерахованих тут нових функцій для C # 7.0 , "відкидання" тепер дозволено як параметри у вигляді _, щоб ви ігнорували параметри, які вас не цікавлять:

p.GetCoordinates(out var x, out _); // I only care about x

PS Якщо вас також плутають із частиною "out var x", прочитайте нову функцію про "Out Variables" також за посиланням.


це _ або * "p.GetCoordinate (out int x, out *); // Мене хвилює лише x"
Onur Topal

схоже, я шукав старішу версію документа.
Онур Топал

2

Ні, але ви можете використовувати делегата (наприклад Action) як альтернативу.

Частково натхненний відповіддю Робіна Р, коли стикався з ситуацією, коли мені здалося, що я хочу необов'язковий параметр, я замість цього використовував Actionделегата. Я запозичив його прикладний код для зміни для використання Action<int>, щоб показати відмінності та схожість:

public string foo(string value, Action<int> outResult = null)
{
    // .. do something

    outResult?.Invoke(100);

    return value;
}

public void bar ()
{
    string str = "bar";

    string result;
    int optional = 0;

    // example: call without the optional out parameter
    result = foo (str);
    Console.WriteLine ("Output was {0} with no optional value used", result);

    // example: call it with optional parameter
    result = foo (str, x => optional = x);
    Console.WriteLine ("Output was {0} with optional value of {1}", result, optional);

    // example: call it with named optional parameter
    foo (str, outResult: x => optional = x);
    Console.WriteLine ("Output was {0} with optional value of {1}", result, optional);
}

Це має перевагу в тому, що необов'язкова змінна відображається у джерелі як звичайний int (компілятор загортає її у клас закриття, а не ми явно загортаємо її у визначений користувачем клас).

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

Він не підходить для всіх випадків використання, але добре працював у моєму випадку реального використання (функція, яка надає дані для одиничного тесту, і де для нового тесту одиниці потрібен доступ до якогось внутрішнього стану, який не міститься у зворотному значенні).


0

Використовуйте перевантажений метод без параметра out для виклику одного з параметром out для C # 6.0 і нижче. Я не впевнений, чому C # 7.0 для .NET Core навіть є правильною відповіддю для цього потоку, коли його спеціально запитали, чи може C # 4.0 мати необов'язковий параметр виходу. Відповідь НІ!


-2

Що з цим?

public bool OptionalOutParamMethod([Optional] ref string pOutParam)
{
    return true;
}

Вам все одно потрібно передати значення параметру від C #, але це необов'язковий параметр ref.


8
"Ви все одно повинні передати значення параметру від C #" ... Це робить його необов'язковим.
Артуро Торрес Санчес

C # ігнорує [Optional]примітку. Це не допомагає.
ToolmakerSteve

-4
void foo(ref int? n)
{
    return null;
}

1
Будь ласка, додайте пояснення до свого коду, щоб усі зрозуміли поняття легко
techspider

1
Хоча цей код може відповісти на питання, надаючи додатковий контекст щодо того, чому та / або як він відповідає на питання, значно покращить його довгострокове значення. Будь ласка , змініть свій відповідь , щоб додати деякі пояснення.
Toby Speight

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