Як явно відкинути аргумент out?


99

Я телефоную:

myResult = MakeMyCall(inputParams, out messages);

але насправді мене не хвилюють повідомлення. Якби це був вхідний параметр, мені було все одно, я просто передав би нуль. Якби це було повернення, про яке я не дбав, я б просто залишив це.

Чи є спосіб зробити щось подібне з out, або мені потрібно оголосити змінну, яку я потім проігнорую?


Подібне запитання тут: stackoverflow.com/questions/2870544/…
Dunc,


Дякую! Соромно, що це не в останній версії.
Ендрю Дакер,

Відповіді:


99

Починаючи з C # 7.0, можна уникнути попереднього декларування параметрів, а також їх ігнорування.

public void PrintCoordinates(Point p)
{
    p.GetCoordinates(out int x, out int y);
    WriteLine($"({x}, {y})");
}

public void PrintXCoordinate(Point p)
{
    p.GetCoordinates(out int x, out _); // I only care about x
    WriteLine($"{x}");
}

Джерело: https://blogs.msdn.microsoft.com/dotnet/2017/03/09/new-features-in-c-7-0/


1
На жаль, це не включено до C # 7 (станом на квітень 2017 р.).
тіа

2
@tia. Я оновив свою відповідь. Очевидно, символ підстановки змінено з *на _. Вибачте, пройшло так багато часу.
Нолонар

14
Вони повинні були дотримуватися своєї ідеї використовувати out voidдля синтаксису, і підкреслення здається дивним вибором.
Девід Андерсон,

1
@ DavidAnderson-DCOM Незважаючи на те, що я не шанувальник підкреслення, я думаю, що їм потрібно ім'я типу для вирішення проблем із перевантаженням.
Джонатан Аллен

Схоже, він все ще створює параметр, оскільки я можу додати рядок _ = {a value};після виклику функції без помилок компіляції.
Bip901

37

На жаль, вам потрібно щось передати, оскільки для його встановлення потрібен метод. Отже, ви не можете надсилати, nullоскільки метод, необхідний для його встановлення, підірветься.

Одним із підходів, щоб приховати потворність, було б обернути метод іншим методом, який робить outпараметр для вас таким:

String Other_MakeMyCall(String inputParams)
{
    String messages;

    return MakeMyCall(inputParams, out messages);
}

Тоді ви зможете зателефонувати Other_MakeMyCallбез необхідності возитися з outнепотрібними вам параметрами.


37

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

Ви використовували слово "dispose" у питанні, яке, на мою думку, було просто невдалим - але якщо параметр out має тип, що реалізує IDisposable, вам слід обов'язково викликати Dispose, якщо в документації методу явно не вказано, що отримання значення не надає право власності. Я не пам’ятаю, щоб коли-небудь бачив метод із одноразовим outпараметром, тому я сподіваюся, що це був просто невдалий вибір слів.


Більш прагматичний анти-шаблон
Джодрелл,

11

Якщо вихідна функція оголошена так:

class C
{
    public Result MakeMyCall(Object arg, out List<String> messages);
}

Ви можете оголосити метод розширення таким чином:

static class CExtension
{
    public static Result MakeMyCall(this C obj, Object arg)
    {
        List<String> unused;
        return obj.MakeMyCall(arg, out unused);
    }
}

Метод розширення поводитиметься як перевантаження, що робить параметр out необов’язковим.


4

Компілятор Visual Basic робить це, створюючи фіктивну змінну. C # міг би це зробити, якщо ви зможете переконати Microsoft у своїй хорошій ідеї.


0

Якщо клас messagesреалізує IDisposable, ви не повинні ігнорувати його. Розглянемо щось на зразок наступного підходу (може бути синтаксично неправильним, оскільки я давно не писав C #):

using (FooClass messages) {
    myResult = MakeMyCall(inputParams, messages);
}

Вийшовши за межі usingблоку, messagesвін буде утилізований автоматично.


1
Цікаво. Але чи не потрібно ініціалізувати змінну в операторі using?
OregonGhost

1
@OregonGhost: Так, ти хочеш. І якщо ви зміните значення змінної в операторі using, це все одно буде розміщено вихідне значення.
Джон Скіт,

@JonSkeet Не вдалося зрозуміти, що все ще зберігається оригінальне значення.
Орхан Аліханов

@OrkhanAlikhanov: Компілятор в основному робить копію змінної на початку usingвисловлювання. Отже, зміна значення цієї змінної в блоці не змінює, який об’єкт буде утилізований.
Джон Скіт,

До речі, там повинно бути out messages.
Орхан Аліханов

0

Ви повинні передати змінну для параметра out. Вам не потрібно ініціалізувати змінну перед передачею:

MyMessagesType messages;
myResult = MakeMyCall(inputParams, out messages); 

Як правило, ви можете просто ігнорувати `` повідомлення '' після дзвінка - якщо `` повідомлення '' не потребує утилізації з якихось причин, наприклад, використання обмежених системних ресурсів, і в цьому випадку вам слід викликати Dispose ():

messages.Dispose();

Якщо він може використовувати значний обсяг пам'яті, і він деякий час залишатиметься в області дії, йому, ймовірно, слід встановити значення null, якщо це посилальний тип, або новий екземпляр за замовчуванням, якщо це тип значення, так що сміття колектор може відновити пам'ять:

messages = null; // Allow GC to reclaim memory for reference type.

messages = new MyMessageType(); // Allow GC to reclaim memory for value type.

0

У цьому випадку я зробив загальний метод розширення для ConcurrentDictionary, який не має методу Delete або Remove.

//Remove item from list and ignore reference to removed item
public static void TryRemoveIgnore<K,T>(this ConcurrentDictionary<K,T> dictionary, K key)
{
    T CompletelyIgnored;
    dictionary.TryRemove(key, out CompletelyIgnored);
}

При виклику з екземпляра ConcurrentDictionary:

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