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


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

8 182

8 ответов:

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

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

фиксированный параметр:
    атрибуты opt

нет.

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

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

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

на самом деле есть способ сделать это, что разрешено 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#, то это позволит вам сделать именно это.

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

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

P.S. Если вы также путаете с частью "out var x", прочитайте новую функцию о" Out Variables " по ссылке.

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

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

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

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

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

Как насчет такой?

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

вам все равно нужно передать значение параметру из C#, но это необязательный параметр ref.

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