Ssh Quack
Ssh Quack

Reputation: 911

How to implicitly convert nullable type to non-nullable type

I have a nullable c# 10 .net 6 project with an extension method to ThrowIfNull

using System;
using System.Runtime.CompilerServices;

#nullable enable
public static class NullExtensions
{
    public static T ThrowIfNull<T>(
        this T? argument, 
        string? message = default, 
        [CallerArgumentExpression("argument")] string? paramName = default
    )
    {
        if (argument is null)
        {
            throw new ArgumentNullException(paramName, message);
        }
        else
        {
            return argument;
        }
    }
}

The extension method implicitly converts string? to string but it does not work for other primitive types like int? or bool?

public class Program
{
    public static void Main()
    {
        Console.WriteLine("Hello World");
        
        string? foo = "foo";
        string nonNullableFoo = foo.ThrowIfNull();  // success from "string?" to "string"
        Console.WriteLine(nonNullableFoo);
        
        bool? baz = true;
        bool nonNullableBaz = baz.ThrowIfNull();    // success from "string?" to "string"
        Console.WriteLine(nonNullableFoo);
        
        int? bar = 2;
        int nonNullableBar = bar.ThrowIfNull(); // error: Cannot implicitly convert type 'int?' to 'int'
        Console.WriteLine(nonNullableBar);
    }
}

How do I make the extension implicitly convert int? and bool??

Here is the full dotnet fiddle https://dotnetfiddle.net/LiQ8NL

Upvotes: 8

Views: 23660

Answers (4)

user25129316
user25129316

Reputation: 9

If you can't proceed without a successful non-null assignment you could use this statement:

int result = getNullableIntResult() ?? throw new Exception("Invalid Result");

Upvotes: 0

Sercan
Sercan

Reputation: 5081

Using the null-coalescing Operator ??

To assign a nullable value to a non-null variable, consider the following code:

int? value = 28;
int result = value ?? -1;
Console.WriteLine($"The result is {result}");
  • Output: The result is 28
int? value = null;
int result = value ?? -1;
Console.WriteLine($"The result is {result}");
  • Output: The result is -1

Editing The Code

Rearrange the code as follows. Therefore bool? You can use the type implicitly:

int? bar = 2;
int nonNullableBar = bar ?? -1; 
Console.WriteLine(nonNullableBar);
bool? baz = true;
bool nonNullableBaz = false;
        
if (baz == true){ 
    nonNullableBaz = true;
} 
else if(baz == false){ 
    nonNullableBaz = false;
} 
else {
    /* Something */
}

References

Upvotes: 5

Eric J.
Eric J.

Reputation: 150108

You can achieve your goal by providing one extension method for non-nullable reference types and another for unmanaged (e.g. int, bool, ...) types. Note that unmanaged types require a cast.

public static class NullExtensions
{
    public static T ThrowIfNull<T>(
        this T? argument,
        string? message = default,
        [CallerArgumentExpression("argument")] string? paramName = default
    ) where T : notnull
    {
        if (argument is null)
        {
            throw new ArgumentNullException(paramName, message);
        }
        else
        {
            return argument;
        }
    }

    public static T ThrowIfNull<T>(
        this T? argument,
        string? message = default,
        [CallerArgumentExpression("argument")] string? paramName = default
    ) where T : unmanaged
    {
        if (argument is null)
        {
            throw new ArgumentNullException(paramName, message);
        }
        else
        {
            return (T)argument;
        }
    }
}

Use like this:

int? foo = 42;
int bar = foo.ThrowIfNull();
Console.WriteLine(bar);

string? baz = "Hello";
string quus = baz.ThrowIfNull();
Console.WriteLine(quus);

// Comment out either this
baz = null;
quus = baz.ThrowIfNull();
// Or this
foo = null;
bar = foo.ThrowIfNull();

Upvotes: 10

Ssh Quack
Ssh Quack

Reputation: 911

Found one possible option in MS dotnet docs https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters

Constraint "where T : struct" The type argument must be a non-nullable value type

Also according to this answer https://stackoverflow.com/a/8745492/2877168 it is not possible to make this work for string? and all other value types. So only way is to define two extension methods. Here is the updated version that works for string? and int?, bool? etc

public static class NullExtensions
{
    public static T ThrowIfNull<T>(this T? argument, string? message = default, [CallerArgumentExpression("argument")] string? paramName = default)
        where T : struct
    {
        if (argument is null)
            throw new ArgumentNullException(paramName, message);
        return (T)argument;
    }

    public static string ThrowIfNull(this string? argument, string? message = default, [CallerArgumentExpression("argument")] string? paramName = default)
    {
        if (argument is null)
            throw new ArgumentNullException(paramName, message);
        return argument;
    }
}

A working version of this is at https://dotnetfiddle.net/uBX1w6

Upvotes: 0

Related Questions