Убедитесь, что объект не является нулевым - PullRequest
0 голосов
/ 08 ноября 2011

Как я могу убедиться, что определенный экземпляр класса никогда не будет нулевым?Кто-то сказал мне использовать Debug.Assert (), но при этом я бы только гарантировал, что код работает в режиме отладки, тогда как я хочу также обеспечить условие «никогда не будет пустым» в выпуске.

Например, в прошлом я писал код вроде:

public string MyString
{
get
{
    if(instance1.property1.Equals("bla"))
    {
        return bla; 
    }
}
}

Но это вызывает исключение, если instance1 равен нулю.Я хотел бы избежать подобных ошибок и создания таких исключений в будущем.

Спасибо,


Пожалуйста, посмотрите конкретный пример ниже, который иллюстрирует проблему:

У меня есть метод, который аутентифицирует пользователей на основе ответов от сервера.Метод таков:

        /// <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.

Спасибо,

Ответы [ 6 ]

6 голосов
/ 08 ноября 2011

Я думаю, вам нужно понять, как instance1, а затем и property1 будут созданы, и создавать их только таким образом, чтобы они не могли быть нулевыми. Обычно это делается путем проверки аргументов при построении, например ::100100

public instance1(string property1)
{
    if (property1 == null) throw new ArgumentNullException("property1");

    this.property1 = property1;
}

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

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

Еще одна вещь, которую следует учитывать, это то, в каком состоянии может существовать ваш класс, в котором обязательное состояние операции, в отличие от необязательного состояния операции. То, что члены обязаны для работы вашего класса, и вы должны постараться спроектировать свои классы так, чтобы они всегда имели требуемое состояние, например ::11017*

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 - это маршрут, но позволяет будь проще).

Если бы я ввел необязательное поле 1028 *, я бы позволил ему изменять состояние моего экземпляра 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).

2 голосов
/ 08 ноября 2011

Вы можете использовать:

if ( instance1 != null && instance1.property1.Equals("bla")){
   // Your code 
 } 
1 голос
/ 08 ноября 2011

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

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

public class Person {
  private readonly string firstName;
  private readonly string lastName;
  private readonly Nubbin optionalNubbin;
}

... где из названия ясно, что необязательный Nubbin может быть нулевым.

Последний и наиболее радикальный подход заключается в написании кода, который не допускает нулевое значение. Вы можете изобрести дуал Nullable , а именно:

public struct NonNullable<T> {
  ...
}

Реализация может работать несколькими различными способами (либо с помощью явного свойства Value, либо, возможно, с использованием перегрузки операторов), но в любом случае задача NonNullable состоит в том, чтобы никогда не позволять кому-либо устанавливать его в null.

1 голос
/ 08 ноября 2011

Лично я бы использовал ?? оператор (при условии property1 является строкой)

public string MyString
{
    get { instance1.property1 ?? "Default value"; }
}
0 голосов
/ 08 ноября 2011

вы можете сделать что-то вроде ниже.

public string MyString
{
    get
    {
        if(instance!=null && instance1.property1.Equals("bla"))
        {
            return "bla"; 
        }
        else 
        {
            return String.Empty; 
        }
    }
}

Это в первую очередь проверит, является ли экземпляр пустым или нет.

0 голосов
/ 08 ноября 2011

Так как instance1.property1 никогда не должно быть нулевым, посмотрите, есть ли способ его правильно инициализировать, а затем выведите ArgumentNullException, если кто-то попытается установить его в ноль.

Пример:

public string Property1
{
    set 
    {
      if(value == null)
      {
        throw new ArgumentNullException();
      } 
      instance1.property1 = value;
    }
}
...