Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cleaner way to do a null check in C#? [duplicate]

Suppose, I have this interface,

interface IContact
{
    IAddress address { get; set; }
}

interface IAddress
{
    string city { get; set; }
}

class Person : IPerson
{
    public IContact contact { get; set; }
}

class test
{
    private test()
    {
        var person = new Person();
        if (person.contact.address.city != null)
        {
            //this will never work if contact is itself null?
        }
    }
}

Person.Contact.Address.City != null (This works to check if City is null or not.)

However, this check fails if Address or Contact or Person itself is null.

Currently, one solution I could think of was this:

if (Person != null && Person.Contact!=null && Person.Contact.Address!= null && Person.Contact.Address.City != null)

{ 
    // Do some stuff here..
}

Is there a cleaner way of doing this?

I really don't like the null check being done as (something == null). Instead, is there another nice way to do something like the something.IsNull() method?

like image 921
now he who must not be named. Avatar asked Sep 25 '22 04:09

now he who must not be named.


People also ask

Why is null check good practice?

It is a good idea to check for null explicitly because: You can catch the error earlier. You can provide a more descriptive error message.

Does any check for null C#?

When calling Any() on a null object, it throws an ArgumentNullException in C#. If the object is null, there definitely aren't 'any', and it should probably return false.

How do you check for null in C?

Historically, C represented NULL as the number 0 (that is, 0x00). Nowadays it can get a bit more complicated, and varies by operating system. You can usually check for NULL using ptr == 0, but there are corner cases where this can cause an issue.

What is the best way to avoid null checks in programming?

Avoid null checks. :) Null checks (and other forms of Defensive Programming) clutter code up, and actually make it more error prone than other error-handling techniques. My favorite technique when it comes to object pointers is to use the Null Object pattern.

How do I remove a null check in a method?

One way to remove null checks in methods is to encapsulate their functionality elsewhere. One way to do this is through getters and setters. For instance, instead of doing this: class Person : IPerson { public IContact contact { get { // This initializes the property if it is null.

What is the purpose of a null check?

The other answers pretty much covered your exact question. A null check is made to be sure that the pointer you received actually points to a valid instance of a type (objects, primitives, etc). I'm going to add my own piece of advice here, though. Avoid null checks.


3 Answers

In a generic way, you may use an expression tree and check with an extension method:

if (!person.IsNull(p => p.contact.address.city))
{
    //Nothing is null
}

Full code:

public class IsNullVisitor : ExpressionVisitor
{
    public bool IsNull { get; private set; }
    public object CurrentObject { get; set; }

    protected override Expression VisitMember(MemberExpression node)
    {
        base.VisitMember(node);
        if (CheckNull())
        {
            return node;
        }

        var member = (PropertyInfo)node.Member;
        CurrentObject = member.GetValue(CurrentObject,null);
        CheckNull();
        return node;
    }

    private bool CheckNull()
    {
        if (CurrentObject == null)
        {
            IsNull = true;
        }
        return IsNull;
    }
}

public static class Helper
{
    public static bool IsNull<T>(this T root,Expression<Func<T, object>> getter)
    {
        var visitor = new IsNullVisitor();
        visitor.CurrentObject = root;
        visitor.Visit(getter);
        return visitor.IsNull;
    }
}

class Program
{
    static void Main(string[] args)
    {
        Person nullPerson = null;
        var isNull_0 = nullPerson.IsNull(p => p.contact.address.city);
        var isNull_1 = new Person().IsNull(p => p.contact.address.city);
        var isNull_2 = new Person { contact = new Contact() }.IsNull(p => p.contact.address.city);
        var isNull_3 =  new Person { contact = new Contact { address = new Address() } }.IsNull(p => p.contact.address.city);
        var notnull = new Person { contact = new Contact { address = new Address { city = "LONDON" } } }.IsNull(p => p.contact.address.city);
    }
}
like image 235
Toto Avatar answered Oct 18 '22 02:10

Toto


Your code may have bigger problems than needing to check for null references. As it stands, you are probably violating the Law of Demeter.

The Law of Demeter is one of those heuristics, like Don't Repeat Yourself, that helps you write easily maintainable code. It tells programmers not to access anything too far away from the immediate scope. For example, suppose I have this code:

public interface BusinessData {
  public decimal Money { get; set; }
}

public class BusinessCalculator : ICalculator {
  public BusinessData CalculateMoney() {
    // snip
  }
}

public BusinessController : IController {
  public void DoAnAction() {
    var businessDA = new BusinessCalculator().CalculateMoney();
    Console.WriteLine(businessDA.Money * 100d);
  }
}

The DoAnAction method violates the Law of Demeter. In one function, it accesses a BusinessCalcualtor, a BusinessData, and a decimal. This means that if any of the following changes are made, the line will have to be refactored:

  • The return type of BusinessCalculator.CalculateMoney() changes.
  • The type of BusinessData.Money changes

Considering the situation at had, these changes are rather likely to happen. If code like this is written throughout the codebase, making these changes could become very expensive. Besides that, it means that your BusinessController is coupled to both the BusinessCalculator and the BusinessData types.

One way to avoid this situation is rewritting the code like this:

public class BusinessCalculator : ICalculator {
  private BusinessData CalculateMoney() {
    // snip
  }
  public decimal CalculateCents() {
    return CalculateMoney().Money * 100d;
  }
}

public BusinessController : IController {
  public void DoAnAction() {
    Console.WriteLine(new BusinessCalculator().CalculateCents());
  }
}

Now, if you make either of the above changes, you only have to refactor one more piece of code, the BusinessCalculator.CalculateCents() method. You've also eliminated BusinessController's dependency on BusinessData.


Your code suffers from a similar issue:

interface IContact
{
    IAddress address { get; set; }
}

interface IAddress
{
    string city { get; set; }
}

class Person : IPerson
{
    public IContact contact { get; set; }
}

class Test {
  public void Main() {
    var contact = new Person().contact;
    var address = contact.address;
    var city = address.city;
    Console.WriteLine(city);
  }
}

If any of the following changes are made, you will need to refactor the main method I wrote or the null check you wrote:

  • The type of IPerson.contact changes
  • The type of IContact.address changes
  • The type of IAddress.city changes

I think you should consider a deeper refactoring of your code than simply rewriting a null check.


That said, I think that there are times where following the Law of Demeter is inappropriate. (It is, after all, a heuristic, not a hard-and-fast rule, even though it's called a "law.")

In particular, I think that if:

  1. You have some classes that represent records stored in the persistence layer of your program, AND
  2. You are extremely confident that you will not need to refactor those classes in the future,

ignoring the Law of Demeter is acceptable when dealing specifically with those classes. This is because they represent the data your application works with, so reaching from one data object into another is a way of exploring the information in your program. In my example above, the coupling caused by violating the Law of Demeter was much more severe: I was reaching all the way from a controller near the top of my stack through a business logic calculator in the middle of the stack into a data class likely in the persistence layer.

I bring this potential exception to the Law of Demeter up because with names like Person, Contact, and Address, your classes look like they might be data-layer POCOs. If that's the case, and you are extremely confident that you will never need to refactor them in the future, you might be able to get away with ignoring the Law of Demeter in your specific situation.

like image 65
Kevin Avatar answered Oct 18 '22 00:10

Kevin


in your case you could create a property for person

public bool HasCity
{
   get 
   { 
     return (this.Contact!=null && this.Contact.Address!= null && this.Contact.Address.City != null); 
   }     
}

but you still have to check if person is null

if (person != null && person.HasCity)
{

}

to your other question, for strings you can also check if null or empty this way:

string s = string.Empty;
if (!string.IsNullOrEmpty(s))
{
   // string is not null and not empty
}
if (!string.IsNullOrWhiteSpace(s))
{
   // string is not null, not empty and not contains only white spaces
}
like image 48
Koryu Avatar answered Oct 18 '22 00:10

Koryu