Убедитесь, что объект не является нулевым
Как я могу убедиться, что определенный экземпляр класса никогда не будет нулевым? Кто-то сказал мне использовать Debug.Assert(), но при этом я бы только гарантировал, что код работает в режиме отладки, в то время как я хочу также обеспечить условие "никогда не будет пустым" в выпуске.
Например, в прошлом я писал такой код:
public string MyString
{
get
{
if(instance1.property1.Equals("bla"))
{
return bla;
}
}
}
Но это вызывает исключение, если instance1 имеет значение null. Я хотел бы избежать подобных ошибок и создания таких исключений в будущем.
Спасибо,
пожалуйста, посмотрите конкретный пример ниже, который иллюстрирует проблему:
У меня есть метод, который проверяет подлинность пользователей на основе ответов от сервера. Метод таков:
/// <summary>
/// attempts authentication for current user
/// </summary>
/// <returns></returns>
public AuthResult CheckUser()
{
WebRequest request = WebRequest.Create(GetServerURI);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
string postdata = "data=" + HttpUtility.UrlEncode(SerializeAuth());
byte[] arr = Utils.AppDefaultEncoding.GetBytes(postdata);
request.ContentLength = arr.Length;
request.Timeout = Convert.ToInt32(TimeUtils.GetMiliseconds(10, TimeUtils.TimeSelect.Seconds));
Stream strToWrite = request.GetRequestStream();
strToWrite.Write(arr, 0, arr.Length);
WebResponse response = request.GetResponse();
using (Stream dataFromResponse = response.GetResponseStream())
{
using (StreamReader reader = new StreamReader(dataFromResponse))
{
string readObj = reader.ReadToEnd();
return DeserializeAuth(readObj);
}
}
}
чтобы вызвать этот метод, я использую
_authenticationResult = authObj.CheckUser();
У меня также есть это свойство, среди других
public ResultType AuthResult
{
get
{
if (_authenticationResult.auth == "1")
return ResultType.Success;
if (_authenticationResult.auth == "0")
return ResultType.FailAccountExpired;
if (_authenticationResult.auth == "-1")
return ResultType.FailWrongUsernameOrPassword;
if (_authenticationResult.auth == "-2")
return ResultType.Banned;
return ResultType.NoAuthDone;
}
}
public enum ResultType { Success, FailWrongUsernameOrPassword, FailAccountExpired, NoAuthDone, Banned }
произошло то, что _authenticationResult был равен null один раз, а свойство AuthResult выдало nullref при попытке "null.auth". Как я могу гарантировать (возможно, внутри метода CheckUser()), что он никогда не возвращает ноль.
Когда я отлаживал приложение, этого никогда не было. Но в производственном процессе, когда время ожидания сервера истекло, иногда метод возвращает значение NULL.
Спасибо,
7 ответов
Я думаю, вам нужно понять, как instance1
и впоследствии property1
будут создаваться, и создавать их только таким образом, чтобы они не могли быть нулевыми. Обычно это делается путем проверки аргументов при построении, например:
public instance1(string property1)
{
if (property1 == null) throw new ArgumentNullException("property1");
this.property1 = property1;
}
Если вы создаете свои типы таким образом, что они не могут существовать в недопустимом состоянии, вы гарантируете, что ваш зависимый код не перевернется на null
ценности.
В противном случае нам нужно было бы увидеть более полный пример того, что вы делаете, чтобы дать вам более конкретный совет.
Другая вещь, которую следует учитывать, это то, в каком состоянии может существовать ваш класс, в каком состоянии требуется операция, а не в дополнительном состоянии операции. То есть, какие члены необходимы для работы вашего класса, и вы должны постараться спроектировать свои классы так, чтобы они всегда имели требуемое состояние, например:
public class Person
{
public Person(string forename, string surname)
{
if (forename == null) throw new ArgumentNullException("forename");
if (surname == null) throw new ArgumentNullException("surname");
Forename = forename;
Surname = surname;
}
public string Forename { get; private set; }
public string Surname { get; private set; }
}
В моем примере типа, я требую, чтобы мой Forename
а также Surname
значение имеет ненулевое значение. Это осуществляется через мой конструктор... мой Person
Тип никогда не может быть создан с нулевыми значениями (хотя, возможно, пустые значения так же плохи, поэтому проверка IsNullOrWhiteSpace
и бросать соответствующий ArgumentException
это маршрут, но давайте сохраним его простым).
Если бы я ввел необязательное поле, я бы позволил ему изменить состояние моего Person
Например, дать ему сеттер:
public class Person
{
public Person(string forename, string surname)
{
if (forename == null) throw new ArgumentNullException("forename");
if (surname == null) throw new ArgumentNullException("surname");
Forename = forename;
Surname = surname;
}
public string Forename { get; private set; }
public string Surname { get; private set; }
public string Initial { get; set; }
}
мой Person
Тип по-прежнему обеспечивает обязательные поля для работы, но вводит необязательное поле. Затем я должен принять это во внимание при выполнении операции, которая использует эти элементы:
public override ToString()
{
return Forename + (Initial == null ? String.Empty : " " + Initial) + " " + Surname;
}
(Хотя это не самый лучший пример ToString
).
Ты можешь использовать:
if ( instance1 != null && instance1.property1.Equals("bla")){
// Your code
}
Люди обычно справляются с этой ситуацией одним из трех способов. Худший способ (на мой взгляд) - быть параноиком по отношению к каждой ссылке, на которую вы смотрите, всегда проверять ее на нуль, а затем делать "что-то", если вы встречаете ноль. Проблема такого подхода заключается в том, что вы часто глубоко погружаетесь в какое-то дерево вызовов, и поэтому "что-то", которое вы делаете (например, возвращаете """разумное" "" значение по умолчанию), не только может быть нарушением наслоения, но также, скорее всего, решит проблему, а не заставит ее столкнуться. В этих случаях на самом деле, вероятно, лучше позволить выбрасывать исключение NulLReferenceException, чем пытаться продолжить.
Более разумным является создание соглашения о кодировании, в котором ваши ссылки никогда не бывают нулевыми, за исключением нескольких случаев, когда из контекста очевидно, что они могут быть нулевыми. Кроме того, и, где это возможно, можно сделать свои классы неизменяемыми или в основном неизменяемыми, чтобы все инварианты могли быть выполнены в конструкторе, а остальная часть кода могла продолжать свою жизнь. Например, я мог бы написать:
public class Person {
private readonly string firstName;
private readonly string lastName;
private readonly Nubbin optionalNubbin;
}
... где из названия ясно, что optionNubbin может быть нулевым.
Последний и наиболее радикальный подход заключается в написании кода, который не будет иметь нулевого значения. Вы можете изобрести дуал Nullable
public struct NonNullable<T> {
...
}
Реализация может работать несколькими различными способами (либо с помощью явного свойства Value, либо, возможно, с использованием перегрузки операторов), но в любом случае работа NonNullable состоит в том, чтобы никогда не позволять кому-либо устанавливать его в null.
Поскольку instance1.property1
никогда не должен быть нулевым, посмотрите, есть ли способ инициализировать его правильно, а затем бросить ArgumentNullException
если кто-то пытается установить его на ноль.
Пример:
public string Property1
{
set
{
if(value == null)
{
throw new ArgumentNullException();
}
instance1.property1 = value;
}
}
Вы можете сделать что-то вроде ниже.
public string MyString
{
get
{
if(instance!=null && instance1.property1.Equals("bla"))
{
return "bla";
}
else
{
return String.Empty;
}
}
}
Это в основном проверит сначала, является ли экземпляр нулевым или нет.
Там нет разумного способа сделать то, что вы хотите. В лучшем случае вы могли бы сделать
private SomeClass _mustExist;
public SomeClass MustExist
{
get
{
if (_mustExist == null)
{
_mustExists = new SomeClass();
}
return _mustExist;
}
}
Все, что нужно сделать, это дать вам тест для получения свойства verey и пройти проверку, необходимо проверить, не является ли объект, имеющий указанное выше свойство, нулевым.
В общем, если вам нужно прыгать через такие обручи, то управление вашим жизненным временем нужно еще раз.