Advertisement
Поиск  
Always will be ready notify the world about expectations as easy as possible: job change page
Jul 11, 2021

Never return NULL references from your functions

Автор:
Sasha Mathews
Источник:
Просмотров:
1165

In C#, reference types can be assigned null values. This is something that developers will have to live with until they use classes to create software. Unfortunately, the folks at Microsoft cannot simply disallow null assignment to reference variables at compile time, because that would break the codebase of almost every project.

Returning Null is bad practice

What is actually wrong with returning a null reference from the method?

Let’s take a look at this simple method:

public Order GetOrder(int orderId)
{
    var order = _db.Orders.FirstOrDefault(u => u.OrderId == orderId);
    return order;
}

The FirstOrDefault method silently returns null if no order is found in the database.

There are a couple of problems here:

  • Callers of GetOrder method must implement null reference checking to avoid getting a NullReferenceException when accessing Order class members. But how is the caller supposed to know how to properly implement error handling? Should the caller throw an exception if it receives a null reference or return some error code?
  • When each caller implements the validation logic on its own, it will be duplicated and the chances are high that the logic will not be consistent across all callers. This is a violation of DRY principle.
  • Getting a nullvalue is an ambiguous for caller, because it doesn’t say whether the null is returned due to the bug or due to the fact that the order was not found in the database. Returning null is definitely not a domain-driven design approach.
  • Returning null is often a violation of the fail fast programming principle. The null can appear due to some issue in the application. The issue can even go to production if the developer has not implemented proper exception handling, which can help quickly detect the issue.

Developers can do several things to improve the situation, such as using a nullable reference types, throwing an exception, or using the null object design pattern.

Use Nullable reference types

If the developer really needs to return a null value from the method (in 99% of cases this is not necessary), the return type can be marked as a nullable reference type (the feature was introduced in C# 8.0).

public Order? GetOrder(int orderId)
{
    var order = _db.Orders.FirstOrDefault(u => u.OrderId == orderId);
    return order;
}

Marking a return type as nullable has two main benefits:

  • The method signature clearly states that it can return a null reference. This way, callers know for sure that they need to implement error handling just by looking at the method signature.
  • The compiler will emit a warning if the caller accesses members of the Order class without first checking for a null reference.

Fail fast by throwing an Exception

The fail fast principle is an important principle in software engineering. The idea is pretty simple — as soon as the application detects a problem (null reference), it should throw an exception.

Incorrect orderId can be caused by the following reasons:

  • The auto mapping library is not configured properly to map orderId values.
  • Some code accidentally assigned the wrong value to orderId variable before calling GetOrder method.
  • The frontend does not pass order IDs to the backend.

Each reason described is a bug in the application. Thus, according to the fail fast principle, as soon as the system detects that the orderId is not valid, it should throw an exception.

The easiest way to throw an exception for orders not found is to use the First method instead of FirstOrDefault in the implementation.

public Order GetOrder(int orderId)
{
    var order = _db.Orders.First(u => u.OrderId == orderId);
    return order;
}

But we have another problem: callers will get InvalidOperationException without any valuable information because this is a common .NET exception. The solution for that is to introduce a custom exception type which the developer should throw once the order was not found in the database.

public Order GetOrder(int orderId)
{
    var order = _db.Orders.FirstOrDefault(u => u.OrderId == orderId);
    
    if (order == null)
    {
        throw new OrderNotFoundException($"Order {orderId} was not found in the database.");
    }
    
    return order;
}

To summarize the benefits of the custom exceptions approach:

  • The chances of issues being discovered and fixed prior to deploying a production environment are much higher.
  • The caller will get detailed information about the problem, which will simplify the troubleshooting process.

Use Null object design pattern

Another way to avoid returning null is to use a Null object design pattern.

A null object is an object without behavior like a stub that a developer can return to the caller instead of returning null value.

public Order GetOrder(int orderId)
{
    var order = _db.Orders.FirstOrDefault(u => u.OrderId == orderId);
    
    if (order == null)
    {
        return new Order();
    }
    
    return order;
}

The caller doesn’t need to check the order for null value because a real but empty Order object is returned.

The null object pattern should be carefully considered as an alternative to throwing an exception. Using the pattern is contrary to the fail fast principle.

This pattern should be used only when callers would otherwise check the object for null in order to skip access to its members.

Conclusion

The conclusion is simple — never return null references from your methods.

Похожее
Aug 22, 2021
The following are a set of best practices for using the HttpClient object in .NET Core when communicating with web APIs. Note that probably not all practices would be recommended in all situations. There can always be good reasons to...
Jun 7, 2021
Author: Himanshu Sheth
One of the most challenging things to do is ‘making the right choice.’ Arriving at a decision becomes even more complicated when there are multiple options in front of you☺. The same is the case with choosing a testing framework...
May 31, 2023
Author: Yohan Malshika
Understanding uow to use And and Or operators with Expression TreesAs a C# developer, you may have come across scenarios where you need to build complex logical expressions dynamically based on user input or other dynamic factors. In such cases,...
Jan 10
Author: MESCIUS inc.
In today’s connected world, data is piling up very fast. We are generating more data than ever in human history, and a lot of this data is getting stored in non-relational formats like JSON documents. JSON has become a ubiquitous...
Написать сообщение
Почта
Имя
*Сообщение


© 1999–2024 WebDynamics
1980–... Sergey Drozdov
Area of interests: .NET Framework | .NET Core | C# | ASP.NET | Windows Forms | WPF | HTML5 | CSS3 | jQuery | AJAX | Angular | React | MS SQL Server | Transact-SQL | ADO.NET | Entity Framework | IIS | OOP | OOA | OOD | WCF | WPF | MSMQ | MVC | MVP | MVVM | Design Patterns | Enterprise Architecture | Scrum | Kanban