У нас есть этот код, который при упрощении выглядит следующим образом:
public class Room
{
public Client Client { get; set; }
public long ClientId
{
get
{
return Client == null ? 0 : Client.Id;
}
}
}
public class Client
{
public long Id { get; set; }
}
Теперь у нас есть три точки зрения.
1) Это хороший код, потому что Client
свойство всегда должно быть установлено (т. Е. Не равно нулю), чтобы Client == null
никогда не происходило, и значение 0
Id в любом случае обозначает ложный Id (это мнение автора кода ;-))
2) Вы не можете полагаться на то, что вызывающая сторона знает, что 0
это ложное значение, Id
и когда Client
свойство всегда должно быть установлено, вы должны бросить exception
в, get
когда Client
свойство оказывается пустым
3) Когда Client
свойство всегда должно быть установлено, вы просто возвращаетесь Client.Id
и позволяете коду генерировать NullRef
исключение, когда Client
свойство оказывается пустым.
Что из этого является наиболее правильным? Или есть четвертая возможность?
источник
Ответы:
Пахнет, как будто вы должны ограничить количество состояний, в которых
Room
может быть ваш класс.Тот факт, что вы спрашиваете о том, что делать, когда значение
Client
равно нулю, указывает на то, чтоRoom
пространство состояний слишком велико.Для простоты я бы не допустил, чтобы
Client
свойство любогоRoom
экземпляра когда-либо было нулевым. Это означает, что код внутриRoom
может смело предполагать, чтоClient
никогда не является нулевым.Если по каким-то причинам в будущем
Client
станетnull
сопротивляться желание поддержать это государство. Это увеличит сложность обслуживания.Вместо этого позвольте коду терпеть неудачу и терпеть неудачу быстро. В конце концов, это не поддерживаемое состояние. Если приложение переходит в это состояние, вы уже переступили черту невозврата. Единственное разумное, что можно сделать, - закрыть приложение.
Это может произойти (вполне естественно) в результате необработанного исключения нулевой ссылки.
источник
null
значение наNullObject
(или скорееNullClient
), которое затем будет работать с вашим существующим кодом и позволит вам при необходимости определить поведение для несуществующего клиента. Вопрос в том, является ли случай «без клиента» частью бизнес-логики или нет.Всего несколько соображений:
а) Почему существует геттер специально для ClientId, когда уже есть публичный геттер для самого экземпляра клиента? Я не понимаю, почему информация о ClientId
long
должна быть высечена в камне в подписи Room.б) Что касается второго мнения, вы можете ввести константу
Invalid_Client_Id
.в) Относительно первого и третьего мнения (и, будучи моим главным пунктом): в комнате всегда должен быть клиент? Может быть, это просто семантика, но это звучит неправильно. Возможно, было бы более уместно иметь совершенно отдельные классы для Room и Client и другой класс, который связывает их вместе. Может быть, назначение, бронирование, размещение? (Это зависит от того, что вы на самом деле делаете.) И в этом классе вы можете применить ограничения «должна быть комната» и «должен быть клиент».
источник
Я не согласен со всеми тремя мнениями. Если
Client
никогда не может бытьnull
, тогда даже не позволяйте этому бытьnull
!Client
в конструктореArgumentNullException
в конструктор.Таким образом, ваш код будет выглядеть примерно так:
Это не связано с вашим кодом, но вам, вероятно, было бы лучше использовать неизменную версию
Room
, сделавtheClient
readonly
, а затем, если клиент изменился, создать новую комнату. Это улучшит безопасность потока вашего кода в дополнение к нулевой безопасности других аспектов моего ответа. Смотрите это обсуждение мутабельных против неизменныхисточник
ArgumentNullException
с?NullReferenceException
, он генерируется .Net VM «при попытке разыменования пустой ссылки на объект» . Вы должны броситьArgumentNullException
, который выбрасывается, «когда нулевая ссылка (Nothing в Visual Basic) передается методу, который не принимает ее в качестве допустимого аргумента».readonly
). Я бы только что отредактировал, но я чувствовал, что комментарий поможет лучше объяснить почему (для будущих читателей). Если бы вы еще не дали этот ответ, я бы дал точно такое же решение. Неизменность - также хорошая идея, но это не всегда так просто, как можно надеяться.Следует избегать второго и третьего вариантов - получатель не должен шлепать вызывающего, за исключением того, что он не контролирует его.
Вы должны решить, может ли Клиент быть нулевым. Если это так, вы должны предоставить вызывающей стороне способ проверить, является ли он пустым, прежде чем получить к нему доступ (например, свойство bool ClientIsNull).
Если вы решите, что Client никогда не может иметь значение null, то сделайте его обязательным параметром для конструктора и добавьте туда исключение, если в него передан null.
Наконец, первый вариант также содержит запах кода. Вы должны позволить Клиенту иметь дело со своим собственным идентификатором. Это кажется излишним - кодировать метод получения в классе контейнера, который просто вызывает метод получения в содержащемся классе. Просто представьте Клиента как свойство (в противном случае вы в конечном итоге продублируете все, что Клиент уже предлагает ).
Если Client может быть нулевым, то вы по крайней мере возьмете ответственность перед вызывающей стороной:
источник
Если нулевое свойство Client является поддерживаемым состоянием, рассмотрите возможность использования NullObject .
Но, скорее всего, это исключительное состояние, поэтому вы должны сделать невозможным (или просто не очень удобным) получить нулевое значение
Client
:Однако, если это не поддерживается, не тратьте время на полуобжаренные растворы. В этом случае:
Вы «решили» исключение NullReferenceException здесь, но по высокой цене! Это заставит всех абонентов
Room.ClientId
проверять наличие 0:Странные ошибки могут возникать из-за того, что другие разработчики (или вы сами!) Забывают проверить возвращаемое значение ErrorCode в какой-то момент времени.
Играйте безопасно и быстро. Позвольте выбрасывать исключение NullReferenceException и "изящно" перехватывать его где-то выше в стеке вызовов, вместо того, чтобы вызывающий мог все испортить еще больше ...
С другой стороны, если вы так заинтересованы, чтобы показать,
Client
а такжеClientId
подумать о TellDontAsk . Если вы попросите у объектов слишком много, вместо того, чтобы сказать им, чего вы хотите достичь, вы можете прекратить объединять все вместе, что усложнит изменения в дальнейшем.источник
NotSupportedException
неправильно, вы должны использоватьArgumentNullException
вместо.Начиная с c # 6.0, который сейчас выпущен, вы должны просто сделать это,
Повышение свойства
Client
toRoom
является очевидным нарушением инкапсуляции и принципа DRY.Если вам необходимо получить доступ к
Id
изClient
изRoom
вы можете сделать,Обратите внимание на использование Null условного оператора ,
clientId
будетlong?
, еслиClient
естьnull
,clientId
будетnull
, в противном случаеclientId
будет иметь значениеClient.Id
.источник
clientid
это обнуляемый долго тогда?var clientId = room.Client.Id
будет и безопасно, иlong
.