Какой стиль возврата я должен использовать?

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

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

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

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

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

Ответ 1

Возврат -1.

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

ИЗМЕНИТЬ

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

В моем представлении, возвращающем -1 (при условии, что он всегда будет недопустимым значением), возвращение nullable int или возвращение объекта 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, "намного более эффективного, чем возврат типа с нулевым значением". Я бы подумал, что любая разница будет незначительной.

Ответ 2

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

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

int? i = YourMethodHere(x, y);

Ответ 3

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

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

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

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


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

Ответ 4

Используйте возвращаемое значение с нулевым значением.

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

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

Ответ 5

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

Ответ 6

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

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

Ответ 7

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

Ответ 8

Лучшие опции - это вернуть логическое значение или вернуть null.

например.

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

или,

int? GetTile(int x, int y);

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

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

Ответ 9

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

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

Ответ 10

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

Вот как обычно работает платформа .NET. Ваши абоненты более высокого уровня должны перехватывать ваши исключения.

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

Ответ 11

Я бы разбил его на два метода. Есть что-то вроде CheckTileExists(x,y) и GetTile(x,y). Первый возвращает логическое значение, указывающее, есть ли плитка в данных координатах. Второй метод - это, по сути, тот, о котором вы говорите в своем исходном сообщении, за исключением того, что он должен генерировать исключение при предоставлении недопустимых координат (поскольку это указывает, что вызывающий абонент не вызывал сначала CheckTileExists(), поэтому это законно исключительная ситуация. Ради скорости вы, вероятно, захотите, чтобы эти два метода совместно использовали кеш, так что в случае, если они вызываются один за другим, накладные расходы на функции GetTile() будут незначительными. Я не знаю если у вас уже есть подходящий объект для включения этих методов или, возможно, вы должны сделать им два метода для нового класса. ИМХО, штраф за выполнение этого подхода пренебрежимо мал, и увеличение ясности кода намного перевешивает его.

Ответ 12

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

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