что лучше, используя обнуляемый или логический параметр return + out - PullRequest
6 голосов
/ 26 ноября 2009

Допустим, у меня есть функция, которая должна возвращать некоторое целочисленное значение. но он также может потерпеть неудачу, и мне нужно знать, когда это произойдет.

Какой способ лучше?

public int? DoSomethingWonderful()

или

public bool DoSomethingWonderful(out int parameter)

Возможно, это скорее вопрос стиля, но мне все еще интересно, какой вариант люди выберут.

Редактировать: уточнение, этот код говорит с черным ящиком (давайте назовем его облаком. Нет, черным ящиком. Нет, подождите. Облако. Да). Мне все равно, почему это не удалось. Мне просто нужно знать, есть ли у меня действительное значение или нет.

Ответы [ 12 ]

12 голосов
/ 26 ноября 2009

Мне больше нравится nullable версия, потому что вы можете использовать оператор null coalesce ?? на нем, например ::100100

int reallyTerrible = 0;
var mightBeWonderful = DoSomethingWonderful() ?? reallyTerrible;
5 голосов
/ 26 ноября 2009

Это зависит от того, как, по вашему мнению, должен выглядеть вызывающий код. И, следовательно, для чего используется ваша функция.

Как правило, вам следует избегать аргументов. С другой стороны, было бы неплохо иметь такой код:

int parameter;
if (DoSomething(out paramameter))
{
  // use parameter
}

Если у вас есть обнуляемое int, оно будет выглядеть так:

int? result = DoSomething();
if (result != null)
{
  // use result
}

Это несколько лучше, потому что у вас нет аргумента out, но код, который решает, успешно ли выполнена функция, выглядит не очень очевидным.

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

try
{
  // normal case
  int result = DoSomething()
}
catch (SomethingFailedException ex)
{
  // exceptional case
}

Одним из преимуществ исключения является то, что вы не можете просто игнорировать его. Обычный случай также прост в реализации. Если в исключительном случае вы что-то можете игнорировать, исключать не следует.

Редактировать: Забыл упомянуть: еще одним преимуществом исключения является то, что вы также можете предоставить информацию , почему операция не удалась. Эта информация предоставляется типом исключения, свойствами исключения и текстом сообщения.

3 голосов
/ 26 ноября 2009

Почему бы не выбросить исключение?

2 голосов
/ 26 ноября 2009

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

2 голосов
/ 26 ноября 2009

Это действительно зависит от того, что вы делаете.

Является ли ноль значимым ответом? Если нет, я бы предпочел вызов метода bool TryDoSomethingWonderful(out int). Это соответствует Рамочной.

Если, однако, null является значимым возвращаемым значением, возвращая int? имеет смысл.

2 голосов
/ 26 ноября 2009

Я бы воспользовался вторым, потому что мне, вероятно, нужно сразу узнать, успешно ли выполнен вызов, и в этом случае я бы лучше написал

int x;
if( DoSomethingWonderful( out x ) )
{
    SomethingElse(x);
}

чем

int? x = DoSomethingWonderful();
if( x.HasValue )
{
   SomethingElse(x.Value);
}
2 голосов
/ 26 ноября 2009

Я бы следовал шаблону, используемому в каком-то месте библиотеки .Net, например:

bool int.TryParse(string s, out value)
bool Dictionary.TryGetValue(T1 key, out T2 value)

Так что я бы сказал:

public bool TryDoSomethingWonderful(out int parameter)
1 голос
/ 26 ноября 2009

Я за использование выходного параметра. На мой взгляд, это та ситуация, для которой использование выходных параметров наиболее подходит.

Да, вы можете использовать оператор coalesce, чтобы сохранить ваш код в виде однострочного кода, если и только если у вас есть альтернативное значение, которое вы можете использовать в оставшейся части кода. Я часто нахожу, что это не так, и я предпочел бы выполнить другой путь кода, чем если бы я был в состоянии успешно получить значение.

        int value;
        if(DoSomethingWonderful(out value))
        {
            // continue on your merry way
        }
        else
        {
            // oops
            Log("Unable to do something wonderful");

            if (DoSomethingTerrible(out value))
            {
                // continue on your not-so-merry way
            }
            else
            {
                GiveUp();
            }
        }

Кроме того, если значение, которое я хочу получить, на самом деле обнуляется, то использование функции с выходным параметром и логическим возвращаемым значением, по моему мнению, является самым простым способом определить разницу между «Мне не удалось получить значение "и" полученное значение равно нулю ". Иногда меня волнует это различие, например, в следующем примере:

    private int? _Value;
    private bool _ValueCanBeUsed = false;

    public int? Value
    {
        get { return this._Value; }
        set
        {
            this._Value = value;
            this._ValueCanBeUsed = true;
        }
    }

    public bool DoSomethingTerrible(out int? value)
    {
        if (this._ValueCanBeUsed)
        {
            value = this._Value;
            // prevent others from using this value until it has been set again
            this._ValueCanBeUsed = false;
            return true;
        }
        else
        {
            value = null;
            return false;
        }
    }

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

0 голосов
/ 27 декабря 2011

Мне не нравится шаблон Microsoft "Try", в котором параметр "out" используется для возврата элемента данных. Среди прочего, методы, закодированные таким образом, не могут использоваться в ковариантных интерфейсах. Я предпочел бы видеть метод, закодированный как: T GetValue(out bool Successful) или, возможно, T GetValue(out GetValueErrorEnum result); или T GetValue(out GetValueErrorInfo result);, если может потребоваться что-то помимо истинного / ложного. Поскольку каждый тип данных имеет допустимое значение по умолчанию, нет проблем с решением, что возвращать в случае сбоя функции. Телефонный код может легко сказать:

  bool success;
  var myValue = Thing.GetValue(ref success);
  if (success)
    .. do something using myValue
  else
    .. ignore myValue

Было бы неплохо, если бы .net и C # предлагали истинные ковариантные параметры "копирования" (вызывающий объект выделил бы место для результата, передал бы указатель на это пространство вызываемой функции, а затем скопировал выделенное пространство в переданный -в переменную только после возврата функции).

0 голосов
/ 26 ноября 2009

Интересно, что мое личное мнение значительно зависит от характера метода. В частности, если целью метода является получение единственного значения , в противоположность «выполнению чего-либо».

Ex:

bool GetSerialNumber(out string serialNumber)

против

string GetSerialNumber() // returns null on failure

Второй мне кажется как-то более "естественным", а также:

bool GetDeviceId(out int id)

против

int? GetDeviceId() // returns null on failure`

Но я признаю, что это действительно относится к территории "стиля кодирования".

О, и я тоже предпочел бы бросить исключение:

int GetDeviceId() // throws an exception on read failure

Я до сих пор не знаю, почему они так ошибаются. Можем ли мы обсудить это, Орен? ; -)

...