Какой стиль возврата я должен использовать? - PullRequest
10 голосов
/ 05 июня 2009

Это связано с соглашениями, используемыми в C #.

У меня есть метод, который имеет два параметра (координаты X и Y). Эти координаты представляют положение, в котором может находиться «тайл». Если плитка находится в этих координатах, метод возвращает ее номер. Если в этих координатах нет плитки, мне интересно, как должен вести себя метод.

вижу три варианта:

  1. Используйте исключения. Я могу выдавать исключение каждый раз, когда Метод не находит плитки. Однако, поскольку такая ситуация не редкость, этот вариант является худшим.
  2. Сделайте это старомодным способом C ++ и верните -1, если плитки нет.
  3. Сделайте номер плитки ссылочным параметром и измените тип возвращаемого значения метода на логическое значение, чтобы показать, есть ли плитка или нет. Но это кажется мне немного сложным.

Итак, что мне делать?

Ответы [ 12 ]

23 голосов
/ 05 июня 2009

Вы можете вернуть ноль и проверить это по коду вызова.

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

int? i = YourMethodHere(x, y);
20 голосов
/ 05 июня 2009

Возврат -1.

Это не просто соглашение C ++, оно также распространено в .NET Framework - например, такие методы, как String.IndexOf или свойства, такие как SelectedIndex, для элементов управления, представляющих списки.

EDIT

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

На мой взгляд, возвращение -1 (при условии, что оно всегда будет недопустимым значением), возвращение обнуляемого целого или возвращение объекта Tile - все это приемлемые решения, и вы должны выбрать тот, который наиболее соответствует остальной части вашего приложения , Я не могу представить, чтобы у какого-либо разработчика была бы малейшая трудность с любым из следующего:

int tileNumber = GetTile(x,y);
if  (tileNumber != -1)
{
   ... use tileNumber ...
}


int? result = GetTile(x,y);
if (result.HasValue)
{
    int tileNumber = result.Value; 
   ... use tileNumber ...
}


Tile tile = GetTile(x,y);
if (tile != null)
{
   ... use tile ...
}

Я не уверен, что понимаю комментарий Питера Рудермана об использовании int как «гораздо более эффективный, чем возврат обнуляемого типа». Я бы подумал, что любая разница будет незначительной.

17 голосов
/ 05 июня 2009

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

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

Обнуляемое int является возможной альтернативой ссылочному параметру, но вы создаете объекты с этим, так что, если "ошибка" является обычной, они могут заставить работать больше, чем ссылочный параметр. Как указал Роман в комментарии в другом месте, у вас будут проблемы C # и VB с обнуляемый тип , вводимый слишком поздно для VB, чтобы обеспечить хороший синтаксический сахар, как у C #.

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


Что-то еще, чтобы рассмотреть, является самодокументированием. Использование -1 и исключение являются условием: вам нужно написать документацию, чтобы убедиться, что разработчик знает о них. Использование возврата int? или ссылочного параметра лучше самоописывает себя и не потребует документации для разработчика, чтобы знать, как справиться с ошибкой. Конечно, :) вы всегда должны писать документацию, точно так же, как вы должны ежедневно чистить зубы.

6 голосов
/ 05 июня 2009

Используйте возвращаемое значение, допускающее значение NULL.

int? GetTile(int x, int y) {
   if (...)
      return SomeValue;
   else
      return null;
}

Это самое ясное решение.

3 голосов
/ 05 июня 2009

Если у вашего метода есть доступ к нижележащим объектам мозаики, другой возможностью будет возвращение самого объекта мозаики или значение NULL, если такой мозаики нет.

2 голосов
/ 05 июня 2009

Наилучшие варианты - вернуть логическое значение или вернуть ноль.

, например

bool TryGetTile(int x, int y, out int tile);

или

int? GetTile(int x, int y);

Есть несколько причин, чтобы предпочесть шаблон «TryGetValue». Например, он возвращает логическое значение, поэтому клиентский код невероятно прост, например: if (TryGetValue (out someVal)) {/ * некоторый код * /}. Сравните это с клиентским кодом, который требует жестко запрограммированного сравнения значений дозорного (с -1, 0, нулевым значением, перехватом определенного набора исключений и т. Д.). С этими конструкциями быстро возникают «магические числа», и фактическая связь становится работа по дому.

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

2 голосов
/ 05 июня 2009

Вы можете вернуть -1, так как это довольно распространенный подход на C #. Однако, возможно, было бы лучше на самом деле вернуть плитку, на которую щелкнули, и в случае, если ни одна плитка не была нажата, вернуть ссылку на одноэлементный экземпляр NullTile. Преимущество такого способа состоит в том, что вы даете конкретное значение каждому возвращаемому значению, а не просто как число, которое не имеет внутреннего значения, кроме числового значения. Тип «NullTile» очень специфичен по своему значению, оставляя мало сомнений для других читателей вашего кода.

2 голосов
/ 05 июня 2009

Я бы выбрал вариант 2. Вы правы, создание исключения в таком обычном случае может сказаться на производительности, а использование параметра out и возвращение значения true или false полезно, но не очень удобно для чтения.

Также подумайте о методе string.IndexOf(). Если ничего не найдено, возвращается -1. Я последую этому примеру.

1 голос
/ 05 июня 2009

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

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

0 голосов
/ 05 июня 2009

Возможно ли, что вы создали (или могли бы создать) объект Tile, на который ссылаются по координатам? Если это так, вы можете вернуть ссылку на эту плитку или null, если плитки с указанными координатами нет:

public Tile GetTile(int x, int y) {
    if (!TileExists(x, y)) 
        return null;
    // ... tile lookup here...
}
...