C # 4.0 необязательные аргументы out / ref

212

C # 4.0 допускает необязательные outили refаргументы?

Джо Дейли
источник
2
Ну что ж, в C ++ они фактически есть для параметров «out» - вы можете иметь аргумент адреса, инициализированный нулем, и довольно часто писать библиотечный код, который будет заполнять такую ​​возвращаемую структуру, только если указатель не равен нулю. Это идиома, возвращающаяся к использованию null для «необязательных аргументов» в C API.
Энди Дент
53
@ Эд и все: почему это не имеет смысла? Если функция «возвращает» значение через «out», я не хочу быть вынужденным принять его. Теперь я знаю, что по техническим причинам компилятор все еще должен что-то передавать, но нет причин, по которым он не мог просто создать для меня фиктивную локальную область за моей спиной.
Роман Старков
5
Может быть, это не имеет смысла с точки зрения того, как все реализовано или какой необязательный параметр на самом деле. Но, как сказал Ромкинс, было бы очень неплохо иметь «необязательные аргументы» - разбор на английском, а не на CLR, и это становится разумным и желательно в IMO.
Адам Толли
9
C # нет, но VB.NET делает.
Джейсон
5
Это было избито до смерти, однако я не могу не упомянуть мою поддержку необязательных аргументов. Я довольно привык к необязательным аргументам по ссылке, установив значение по nullумолчанию ( я пришел из PHP ) и протестировав, nullчтобы продолжить заполнение аргумента ( для тех, кто знаком, подумайтеpreg_match() ). В любом случае, хотя я понимаю, с технической точки зрения, в настоящее время это может быть невозможно, и что PHP и C # довольно несопоставимы, это все равно было бы « хорошим » инструментом, доступным.
Дэн Лугг

Ответы:

93

Как уже упоминалось, это просто не разрешено, и я думаю, что это имеет очень хороший смысл. Однако, чтобы добавить некоторые подробности, вот цитата из спецификации C # 4.0 , раздел 21.1:

Формальные параметры конструкторов, методов, индексаторов и типов делегатов могут быть объявлены необязательными:

fixed-параметр:
    атрибуты opt параметр-модификатор opt идентификатор типа default-аргумент opt
default-аргумент:
    = выражение

  • Фиксированный параметр с по умолчанию-аргументом является необязательным параметром , в то время как с фиксированным параметром без по умолчанию-аргумент является обязательным параметром .
  • Обязательный параметр не может появляться после необязательного параметра в списке формальных параметров .
  • 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 за это.)

Dunc
источник
6
Любая идея для более элегантного решения, чем объявление темп / пустышка?
Луис Рис
20
Что в этом не элегантного? Выглядит вполне прилично для меня.
Нейтрино
27
Может быть, приличный, но элегантный, безусловно, в другой лиге. Тот факт, что лучшего решения не существует, не означает, что он является современным по указу.
о0 '.
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
Это очень разумное решение, но нужно помнить одну вещь: компилятор не будет применять требование, чтобы параметр out был назначен до выхода из метода.
Кен Смит
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.

Чтобы это скомпилировать, вам нужно включить небезопасный код в опциях проекта. Это действительно хакерское решение, которое обычно не следует использовать, но если вам нужно какое-то странное, загадочное, таинственное, вдохновленное руководством решение, ДЕЙСТВИТЕЛЬНО нужен необязательный параметр out в C #, тогда это позволит вам сделать именно это.

wizard07KSU
источник
6

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

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

PS, если вас также смущает часть «out var x», прочитайте новую ссылку «Out Variables» по ссылке.

jbdeguzman
источник
это _ или * "p.GetCoordinates (out int x, out *); // Я забочусь только о x"
Onur Topal
Похоже, я искал старую версию документа.
Онур Топал
2

Нет, но вы можете использовать делегата (например Action) в качестве альтернативы.

Вдохновленный ответом Робина Р., когда я столкнулся с ситуацией, когда я думал, что мне нужен необязательный параметр out, я вместо этого использовал 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);
}

Преимущество этого заключается в том, что необязательная переменная появляется в источнике как обычное целое число (компилятор оборачивает его в класс замыкания, а не в явном виде в класс, определенный пользователем).

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

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

Стив
источник
0

Используйте перегруженный метод без параметра out, чтобы вызвать метод с параметром out для C # 6.0 и ниже. Я не уверен, почему C # 7.0 для .NET Core является даже правильным ответом для этой темы, когда его специально спросили, может ли C # 4.0 иметь необязательный параметр out. Ответ - нет!

Mr_CSharp
источник
-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;
}
user6469927
источник
1
Пожалуйста, добавьте некоторые пояснения к своему коду, чтобы все могли легко понять концепцию
techspider
1
Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно того, почему и / или как он отвечает на вопрос, значительно улучшит его долгосрочную ценность. Пожалуйста, отредактируйте свой ответ, чтобы добавить пояснения.
Тоби Спейт
2
Это приведет к синтаксической ошибке, так как метод имеет тип возврата void. Также не отвечает на вопрос.
Натан Монтес