DEV Community

Cover image for No more NullReferenceException for Lists in C#
Ali Alp
Ali Alp

Posted on • Edited on

No more NullReferenceException for Lists in C#

While coding sometimes little tiny things are so frustrating , like forgetting to instantiate a List object before adding a new item, an easy solution will be to write an extension method like this

public static class ListExtension
{
    public static void Add<T>(this List<T> list, T value, out List<T> newInstance)
    {
        if (list == null) list = new List<T>();
        list?.Add(value);
        newInstance = list;
    }
}
Enter fullscreen mode Exit fullscreen mode

and then use it like this

[Test]
public void SafeAddTest()
{
    List<string> list = null;
    list.Add("test",out list);

    Assert.AreEqual(1,list.Count);
}
Enter fullscreen mode Exit fullscreen mode

therefore whenever you want to make sure that the list that you are going to add to it is already instantiated you can simply pass the list instance as the second variable and that's it.

Happy coding :)

Top comments (31)

Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
gfoley83 profile image
Gavin Foley

Under what scenarios would you want a null list?

Collapse
 
Sloan, the sloth mascot
Comment deleted
 
gfoley83 profile image
Gavin Foley

I agree with what you're saying for objects but not for collections. I see no value in having null collections.

Thread Thread
 
Sloan, the sloth mascot
Comment deleted
 
gfoley83 profile image
Gavin Foley • Edited

But collections aren't just objects. They serve a different purpose.
I'm familiar with the builder pattern and I see no need to use nullable lists with it:

gist.github.com/GFoley83/878c2ac59...

Note the parts dictionary and why initializing it saves having to null check everywhere in derived classes.

Best practice with arrays has always been to initialize arrays in any language I've used.

Thread Thread
 
Sloan, the sloth mascot
Comment deleted
 
gfoley83 profile image
Gavin Foley • Edited

Hmmm, no I get the point just fine you're just wrong and too obnoxious to admit it.

You're quoting the builder pattern like it somehow proves your point when it doesn't, nor have you provided anything to support your criticism of the article above. I don't like the extension method personally but your comment promotes bad practice so I thought it would be a good idea to comment.

I can find more articles than can be counted showing why you should initialize arrays in any language.

Builder ex. note how and why the dictionary is initialized

gist.github.com/GFoley83/878c2ac59...

Collapse
 
josemedina760 profile image
Jose Medina

Exactly...

Collapse
 
itr13 profile image
Mikael Klages • Edited

Having a second argument for out, and making sure that argument is always passed back to wherever you got the list from sounds like more work and more prone to error than just getting an NRE.

Some minor improvements:

list = list ?? new List<T>();
list.Add(value); //It can't be null, so ?. isn't needed
Collapse
 
alialp profile image
Ali Alp

The idea behind it is just another way to handle the null reference exception for lists and maybe the one which hasn't been heard of, the example is a dummy example to show that the list is explicitly set to null, depends on the condition it can be useful or useless.
There is no wrong or right ideas, especially when it's working .
I have read all the different methods which has been recommended in the comments , but unfortunately all of them has been heard of and well known.
I encourage everyone to think , why when you are trying to add to a list it should throw an exception ?

Collapse
 
danstur profile image
danstur

"There is no wrong or right ideas, especially when it's working"

As I had to tell students back in university "it's working" is the absolute minimum requirement, but doesn't say anything about the quality of the code.

You're simply solving the wrong problem here. Instead of asking yourself how you can ignore that the list is null, ask yourself why it can be null in the first place.

The only reason for a list to be null in the first place is if null has a special meaning, in which case you can't ignore it. In all other cases simply initialize your variables correctly and you avoid this problem in the first place. Take a look at LINQ and how it always returns empty lists and not null for its op operations.

Collapse
 
alialp profile image
Ali Alp

When you are working with a list as a property of a class , and the class is already instantiated , what is the point of the null reference exception when you explicitly want to add an item to it ?

Thread Thread
 
danstur profile image
danstur • Edited

As I said, you're trying to solve the local problem without looking at the bigger picture.

The first question you should ask yourself is "Why is this list property ever null?" Does null have a specific meaning that's different from an empty list? That's a valid reason, but that's very rarely the case. And if it is, then you have to treat it differently anyhow so that extension method wouldn't make sense either.

But in practice, in almost all situations the best approach is to simply initialize the property with an empty list during construction. This makes code the much simpler to reason about and makes such hacks completely unnecessary.

Thread Thread
 
alialp profile image
Ali Alp

The question is not the difference between null and empty , there is no doubt that both are necessary , but why there is an extra operation in adding an item to a for instance list ,in my opinion all the collections should be auto initialize themselves on adding new item to them , off course i 'm not speaking about get Operation , but when you want to add an item to a list most definitely you want it to be initialized , and in my opinion this should be taken care by the compiler during the compiler code generation , best practices were developed gradually and the process is ongoing :)

Thread Thread
 
danstur profile image
danstur

But there is an important difference between null and empty and your solution peppers over a simple programmer error while hiding that difference.

C# is almost 20 years old by now and it and the whole world is moving towards non-nullability by default. C# 8 makes this easier than ever before.

There simply is no reason to write your code instead of simply doing

public sealed class Foo 
{
    public ICollection<int> SomeInts { get; } = new List<int>(); // there we go, never going to be null.
}

Why should the compiler special case lists? What about Dictionaries? Old StringCollections from the time before generics? What about stringbuilders or custom classes that implement ICollection? What if they don't have a no argument constructor?

A whole lot of complicated questions to answer for a problem for which best practices have existed for decades: Initialize your classes in such a way that all its properties have valid values.

Thread Thread
 
alialp profile image
Ali Alp

Your solution about initializing at object construction contradicting with your own stated argument regarding the difference between null and empty if you initialize the object at the construction you will loose the chance of determining whether the collection ever been used or not, also can cause overhead in some cases in grained memory management, all i'm saying is adding to a list and initializing it when it's null should happen at once and the developer should not be woried about the initializing it when they try to add to it , in c# 8 still you may define a nullable list, therefore the question will remain intact

Thread Thread
 
danstur profile image
danstur • Edited

Read again: What I said is that in some rare situations it makes sense to distinguish between empty and null collections. In almost all situations it does not.

And sure there are incredibly rare situations where the memory pressure of unnecessarily initialising a list will cause performance problems, but the 101 of performance is to first write clean, simple code and only when you actually have a problem profile and start optimising.

I take any bet that you've never come across a situation in your professional development life where that was actually a problem (and adding a branch to every single add of a list is quite awful from a performance point of view too).

And yes in C# 8 you can still define a nullable list, but best practice tells you that you should avoid nullability as much as possible.

But I mean if the fact that pretty much every single comment is negative to this idea and that it goes against best practices in modern C# and nobody else is doing this despite extension methods being around for years (I mean the whole "use extension method to be able to use instance methods on possibly null values" has been done to death - usually for things such as assertions, this is not novel in itself) convinces you this might not be the best idea, I doubt me going on about this will make a difference.

Thread Thread
 
alialp profile image
Ali Alp • Edited

I'm not claiming that the approach of this article is the best solution or following the best practices , just a demonstration of a prototype of a possible better approach. About writing simple and profiling , it depends on the application , simplicity and clean code are guidelines , not an approach , it always depends , you don't need to bet ,what i have faced in my career or you have is not relevant , the approach should be generic , can you think of any condition which you are adding an item to a collection but Don't want to initialize it ?
Because i don't think there is , therefore i believe the first addition should handle the initialization

Thread Thread
 
aaronincincy profile image
Aaron Johnson

I feel like you're being intentionally obtuse about this when everyone else has provided great arguments in favor of always-initializing collections.

To play your game, here's an example:


class LibraryVisitor {
  private List<Book> CheckedOutBooks { get; set; }
  private string MemberNumber { get; set; }

  public void CheckOut(Book book) {
    CheckedOutBooks.Add(book);
  }

  public void Register(memberNumber) {
    MemberNumber = memberNumber;
    CheckedOut = new List<Book>();
  }
}

This is obviously shit code (arguably no worse than your examples), but the premise is I shouldn't be able to check out a book until I've registered.

A reasonable fix to this code would be:


class LibraryVisitor {
  private List<Book> CheckedOutBooks { get; } = new List<Book>();
  private string MemberNumber { get; }

  public void CheckOut(Book book) {
    if (MemberNumber == null) {
      throw new NotRegisteredException();
    }

    CheckedOutBooks.Add(book);
  }

  public void Register(memberNumber) {
    MemberNumber = memberNumber;
  }
}
Thread Thread
 
alialp profile image
Ali Alp

When you don't get , you don't get it.
let me demistify once more.
The conventional methods which any junior developer should be aware are crystal clear . The issue is why when you are trying to add to a list which is a member of a class and you already instantiated the class should throw an exception ?
It should instanciate the list object on the first add itself.

Collapse
 
nallack profile image
cgray

There is now a better way than checking for null.

Upgrading to C# 8 adds support for compiling with non-nullable reference types. Instances that can be null will now need to be declared with Nullable or T? For short.

This is more inline with Typescript and C++ references.

Collapse
 
colinm9991 profile image
Colin M • Edited

If you're forgetting to instantiate objects then you have bigger problems to solve. This solution isn't readable, doesn't have clear intent and the out parameter is just confusing because it's not clear what kind of manipulation is happening when reading the method name. TryAdd would be better but still not clear, AddOrCreate would be one step closer but again, still confusing.

Keep it simple, initialize your objects, write clean code.

Collapse
 
nlaslett profile image
Neil Laslett

Forgetting to initialize lists really become a problem in class properties. The way to solve that is in the class constructor. Any list created in the same code where you're using it should be easy to manage.

Collapse
 
kbiel profile image
Ken in NH

This is a confusing use of "out". Not that I recommend it, but a better way of handling this case is to return the incoming list or new list. This would allow functional code at least. It still would not be obvious that you must replace your list reference with the reference emerging from the method.

If you find that you often forget to instantiate, use "var". In your unit test, it would stop you from getting an "NullReferenceException" as well.

[Test]
public void SafeAddTest()
{
    //var list = null; //This won't even compile!
    var list = new List<string>(); //You are forced to provide type
                                   //through an instance for "var".
    list.Add("test");

    Assert.AreEqual(1,list.Count);
}
Collapse
 
activa profile image
Philippe Leybaert • Edited

This is wrong on so many levels.

If you need a method like this, you have a bigger problem in your code

Collapse
 
webbes profile image
webbes

It is never a good idea to hide programming errors and that is what you are doing in effect. This can and will become a root cause for a very nasty bug that is so difficult to track. If you one day pass the wrong (null) variable, your code pretends that there's no issue at all and continues. It's a bit like "on error resume next" or an empty catch block. Sure an empty catch block will prevent you from ever seeing an error again, so... it "works", but you will also never be sure that your code still functions correctly. If you've got null reference exceptions you are obviously not checking input parameters which you should. If you are an experienced developer, you will notice an uninstantiated variable in the same function in a glance. Trust me. It's like a portrait without a nose.

Collapse
 
jhbertra profile image
Jamie Bertram

Pro tip: until nullable reference types hit primetime, use JetBrains.Annotations and add NotNull / CanBeNull attributes to all reference-type fields, properties, parameters and return types. As long as you run ReSharper and fix all warnings, you should never have a NullReferenceException again.

Even better - install ReCommended extensions for ReSharper, and make it a warning to not annotate reference types. Your code should now be almost NullReferenceException-proof.

Enjoy never worrying about this error again.