Domanda

How can I make sure that a certain instance of a class will never be null? Someone told me to use Debug.Assert() but by doing so, I would only ensure that the code works in debug mode, whereas I want to ensure the is-never-null condition in release as well.

For example, in the past I wrote code like:

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

But this throws an exception if instance1 is null. I would like to avoid making such mistakes and generating such exceptions in the future.

Thanks,


please see a specific example below that illustrates the problem:

I have a method that authenticates users based on responses from a server. The method is this:

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

to call this method, i use

_authenticationResult = authObj.CheckUser();

I also have this property, among others

        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 }

what happened was that _authenticationResult was null once, and the property AuthResult threw a nullref at attempting "null.auth". How can I ensure (perhaps inside the CheckUser() method) that it never returns null.

When i debugged the app it never happened. But in production, when the server timed out sometimes the method returned null.

Thanks,

È stato utile?

Soluzione

I think you need to understand how instance1, and subsequently property1 are going to be instantiated, and only instantiate them in such a way that they cannot be null. This is generally done by checking arguments at construction, e.g.:

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

    this.property1 = property1;
}

If you create your types in such a way that they cannot exist in an invalid state, you ensure that your dependent code won't fall over on null values.

Otherwise, we'd need to see a fuller example of what you are doing to give you more concrete advice.

The other thing to consider, is what state your class can exist in which is a required state of operation, vs. an optional state of operation. That being, what members are required for your class to operate, and you should endeavour to design your classes such that they always have the required state, e.g.:

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

In my example type, I'm requiring that my Forename and Surname value have a non-null value. This is enforced through my constructor... my Person type can never be instantiated with null values (although, perhaps empty values are just as bad, so checking IsNullOrWhiteSpace and throwing an appropriate ArgumentException is the route, but lets keep it simple).

If I were to introduce an optional field, I would allow it to mutate the state of my Person instance, e.g., give it a 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; }
}

My Person type still enforces the required fields for operation, but introduces an optional field. I then need to take this into consideration when performing an operation which uses these members:

public override ToString()
{
  return Forename + (Initial == null ? String.Empty : " " + Initial) + " " + Surname;
}

(Although that is not the greatest example of a ToString).

Altri suggerimenti

You can use:

if ( instance1 != null && instance1.property1.Equals("bla")){
   // Your code 
 } 

Personally, I would use the ?? operator (assuming property1 is a string)

public string MyString
{
    get { instance1.property1 ?? "Default value"; }
}

People generally handle this situation in one of three ways. The worst way (in my opinion) is to be paranoid about every reference you look at, always testing it against null, and then doing ""something"" if you do encounter a null. The problem with such an approach is that you're often deep in some call tree, and so the "something" you do (like return a """reasonable""" default value) not only is likely to be a layering violation, but also is likely to paper over a problem rather than cause it to be confronted. In these cases it's actually probably better to let a NulLReferenceException be thrown rather than make some half-assed attempt to continue.

The saner thing to do is establish a coding convention where your references are never null, except in a few cases where it is obvious from context that they can be null. Additionally, and wherever possible, one can make one's classes immutable or mostly-immutable so that all the invariants can be done in the constructor and the rest of the code can get on with its life. For example, I might write:

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

...where it is clear from the name that optionalNubbin might be null.

The final and most radical approach is to write code that will not admit a null. You could invent the dual of Nullable<T>, namely:

public struct NonNullable<T> {
  ...
}

The implementation could work in a few different ways (either by using an explicit Value property, or perhaps using operator overloading) but in any case the job of NonNullable is to never let someone set it to null.

Since instance1.property1 should never be null, see if there's a way to initialize it properly, then thrown an ArgumentNullException if someone tries to set it to null.

Example:

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

you can do something like below.

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

This will basically check first whether instance is null or not.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top