что лучше, используя nullable или логический параметр return+out

StackOverflow https://stackoverflow.com/questions/1800003

  •  05-07-2019
  •  | 
  •  

Вопрос

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

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

public int? DoSomethingWonderful()

или

public bool DoSomethingWonderful(out int parameter)

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

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

Это было полезно?

Решение

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

int reallyTerrible = 0;
var mightBeWonderful = DoSomethingWonderful() ?? reallyTerrible;

Другие советы

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

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

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
}

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

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

Почему бы не создать исключение?

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

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

Поэтому я бы сказал:

public bool TryDoSomethingWonderful(out int parameter)

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

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

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

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

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

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

чем

int? x = DoSomethingWonderful();
if( x.HasValue )
{
   SomethingElse(x.Value);
}

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

Да, вы можете использовать оператор 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();
            }
        }

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

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

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

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

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

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

Должны ли мы проверять как bool, так и out, или я должен проверять как returns null, так и фактический возврат.

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

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

Бывший:

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top