1

如何确保某个类的某个实例永远不会为空?有人告诉我使用 Debug.Assert() 但这样做,我只会确保代码在调试模式下工作,而我也想确保发布时的 is-never-null 条件。

例如,过去我写的代码如下:

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 在尝试“null.auth”时抛出了一个 nullref。我如何确保(可能在 CheckUser() 方法中)它永远不会返回 null。

当我调试应用程序时,它从未发生过。但是在生产中,当服务器超时时,该方法有时会返回 null。

谢谢,

4

6 回答 6

6

我认为您需要了解如何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; }
}

在我的示例类型中,我要求 myForenameSurnamevalue 具有非空值。这是通过我的构造函数强制执行的......我的Person类型永远不能用空值实例化(尽管空值可能同样糟糕,因此检查IsNullOrWhiteSpace并抛出适当ArgumentException的方法是路线,但让它保持简单)。

如果我要引入一个可选字段,我会允许它改变我的Person实例的状态,例如,给它一个 setter:

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;
}

(尽管这不是 a 的最好例子ToString)。

于 2011-11-08T14:32:47.153 回答
2

您可以使用:

if ( instance1 != null && instance1.property1.Equals("bla")){
   // Your code 
 } 
于 2011-11-08T14:31:02.383 回答
1

就个人而言,我会使用?? 运算符(假设property1是一个字符串)

public string MyString
{
    get { instance1.property1 ?? "Default value"; }
}
于 2011-11-08T14:39:59.670 回答
1

人们通常以三种方式之一处理这种情况。最糟糕的方法(在我看来)是对你看到的每一个引用都保持偏执,总是针对 null 测试它,然后在遇到 null 时执行“某事”。这种方法的问题在于,您经常深入某个调用树,因此您所做的“某事”(例如返回“”“合理”“”默认值)不仅可能违反分层,但也很可能掩盖问题而不是导致它被面对。在这些情况下,实际上最好让 NulLReferenceException 被抛出,而不是半途而废地尝试继续。

更明智的做法是建立一个编码约定,其中您的引用永远不会为空,除非在少数情况下,从上下文中可以明显看出它们可以为空。此外,只要有可能,可以使自己的类不可变或大部分不可变,以便所有不变量都可以在构造函数中完成,其余代码可以继续其生命。例如,我可能会写:

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

...从名称中可以清楚地看出 optionalNubbin 可能为空。

最后也是最激进的方法是编写不接受空值的代码。您可以发明 Nullable<T> 的对偶,即:

public struct NonNullable<T> {
  ...
}

该实现可以以几种不同的方式工作(通过使用显式 Value 属性,或者可能使用运算符重载),但无论如何,NonNullable 的工作是永远不要让某人将其设置为 null。

于 2011-11-08T15:16:38.330 回答
0

因为instance1.property1不应该为空,所以看看是否有办法正确初始化它,然后抛出一个ArgumentNullException如果有人试图将它设置为空。

例子:

public string Property1
{
    set 
    {
      if(value == null)
      {
        throw new ArgumentNullException();
      } 
      instance1.property1 = value;
    }
}
于 2011-11-08T14:32:48.370 回答
0

您可以执行以下操作。

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

这将首先检查实例是否为空。

于 2011-11-08T14:41:30.960 回答