Как явно отказаться от аргумента out?


99

Звоню:

myResult = MakeMyCall(inputParams, out messages);

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

Есть ли способ сделать что-то подобное с выходом, или мне нужно объявить переменную, которую я проигнорирую?


Аналогичный вопрос здесь: stackoverflow.com/questions/2870544/…
Дунк


Спасибо! Жаль, что не в последней версии.
Эндрю Дакер

Ответы:


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 г.).
tia

2
@tia. Я обновил свой ответ. Судя по всему, подстановочный знак был изменен с *на _. Извините, это заняло так много времени.
Nolonar

14
Им следовало придерживаться своей идеи использовать out voidсинтаксис, и подчеркивание кажется странным выбором.
Дэвид Андерсон

1
@ DavidAnderson-DCOM Хотя я не поклонник подчеркивания, я думаю, что им нужно имя типа для разрешения перегрузки.
Джонатан Аллен

Похоже, что он все еще создает параметр, потому что я могу добавить строку _ = {a value};после вызова функции без каких-либо ошибок компиляции.
Bip901 05

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.