2009-06-05 2 views
10

Это связано с соглашениями, используемыми в C#.Какой стиль возврата я должен использовать?

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

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

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

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

+1

Мне кажется, я начал святую войну :) – undsoft

+4

+1 для Святой войны! ;) – Adrien

+0

Ребята, спасибо за ответы. Я не знал о типах с нулевым значением. Я вспомню их позже. Но для того, чтобы знать, что все будет использоваться, я буду использовать опцию «return -1». – undsoft

ответ

20

возвращает -1.

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

EDIT

Просто разработать, из трех вариантов в вашем вопросе (Exception, возвращается -1, из параметра), возвращающая -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 будучи «намного эффективнее, чем возвращать обнуляемый тип» , Я бы подумал, что любая разница будет незначительной.

+3

Это распространено только потому, что в то время, когда эти методы были созданы, или в связи с продолжением общности (поскольку старые привычки трудно избавиться), не существует. Думаю, что Nullables - гораздо более разумный выбор. – configurator

+1

Это также распространено, потому что это намного эффективнее, чем возврат типа с нулевым значением. –

+5

«Обитатели - это гораздо более разумный выбор». Зависит от контекста. Самое главное - соглашаться с соглашениями, которые вы используете в остальной части вашего приложения. – Joe

23

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

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

int? i = YourMethodHere(x, y); 
+1

Возврат null является правильным способом, если в противном случае объект возвращается. –

+0

Это также заставляет метод автоматически указывать возвращаемое значение. Это довольно расточительно для такой простой проблемы. –

+1

«нужно использовать»? Я от всей души не согласен. –

3

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

+0

Это то, что я сделал бы в любом случае, так как вы, возможно, сможете получить доступ к номеру плитки в случае необходимости. –

0

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

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

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

6

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

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

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

17

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

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

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

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


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

+1

Я не согласен с вашим первым заявлением. Исключения - это ситуации, когда ваш метод не может выполнять то, что он обещает сделать. Тот факт, что это должен быть редкий случай, является вторичным. Разумеется, надбавка должна быть обеспечена. –

+2

+1 Далеко лучшее и самое четкое объяснение. –

+2

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

2

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

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

2

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

0

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

+0

Overkill. Также, если это многопоточное приложение, и плитка может появляться/исчезать между вызовами CheckTileExists и GetTile? – Joe

+1

@Joe: если это может произойти, то в чем же преимущество - это возвращаемое значение GetTile(), поскольку оно может просто исчезнуть из-под вас? – rmeador

+0

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

0

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

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

Лучшие вариантов для возврата булевых, а или возвращать нуль.

например.

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

или

int? GetTile(int x, int y); 

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

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

+0

Если вам нужно вернуть false («no tile»), какое значение будет иметь параметр плитки? Конечно, это может быть любое значение, но для типа int это странно. Было бы хорошо, если бы это был объект, чтобы сделать его нулевым и вернуть false. Если это int, и кто-то забывает проверить возвращаемое значение функции, он будет использовать неправильное значение (например, «0» или «-1»). Это не хороший способ кодирования. – Roma

+0

@Roman, я считаю, что ваши рассуждения неверны. TryGetTile() может делать все, что захочет, для выходного значения, если get не работает, скорее всего, он просто не изменит выходной параметр. Говорить, что это более опасно, чем другие методы, довольно смешно. Здесь, конечно, нет никакой большей опасности, чем если GetTile возвращает нуль, и вы забываете проверить значение null перед выполнением операций над возвращаемым значением. В самом деле, именование самого TryGetTile делает его массовым образом ясно, как его следует использовать, и делает такую ​​ошибку намного проще, чем в коде. – Wedge

1

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

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

Смежные вопросы