Advertisement
Always will be ready notify the world about expectations as easy as possible: job change page

Never return NULL references from your functions

Добавлено:
Jul 11, 2021
Автор:
Sasha Mathews
Источник:
Просмотров:
882

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.

Похожее
Jul 7, 2021
Author: Changhui Xu
C# has a feature, String Interpolation, to format strings in a flexible and readable way. The following example demonstrates the way how we usually output a string when we have data beforehand then pass data to the template string.var name...
Apr 16, 2022
Author: Mohsen Saniee
Today, developers are paying more attention to mapping libraries than ever before, because modern architecture forces them to map object to object across layers. For example, in the past I prepared a repository for clean architecture in github. So it’s...
Aug 18, 2023
Author: Alex Maher
C# Data Structures Handbook!Every decision in programming is a silent influencer of performance and clarity. In C#, one such vital choice is selecting the right data structure.Data structures are foundational pillars. These structures are where data lives, breathes, and interacts,...
Nov 30, 2023
Author: Dev·edium
Keeping your C# applications safe and sound.Keeping app secrets safe is always tricky for developers. We want to work on the main parts of the app without getting distracted by secret-keeping. But, the app’s safety is very important. So, what...
Написать сообщение
Почта
Имя
*Сообщение


© 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
LinkedIn
Boosty
Donate to support the project
GitHub account
GitHub profile