Skip to content

[API Proposal]: Additional ArgumentNullException.ThrowIfNull overloads #58047

@DaZombieKiller

Description

@DaZombieKiller

Background and motivation

While migrating code to start using ArgumentNullException.ThrowIfNull, I'm finding it cumbersome that there are no overloads for passing in nullable value types or pointers. This leads to code looking inconsistent because it uses a mix of ThrowIfNull and either custom throw helpers or conditionals, it feels like I'm still dealing with all the problems that ThrowIfNull was added to solve, just for pointers and value types instead of classes.

API Proposal

namespace System
{
    public partial class ArgumentNullException : ArgumentException
    {
        /// <summary>Throws an <see cref="ArgumentNullException"/> if <paramref name="argument"/> is null.</summary>
        /// <param name="argument">The pointer argument to validate as non-null.</param>
        /// <param name="paramName">The name of the parameter with which <paramref name="argument"/> corresponds.</param>
        public static unsafe void ThrowIfNull(void* argument, [CallerArgumentExpression("argument")] string? paramName = null);
        
        /// <summary>Throws an <see cref="ArgumentNullException"/> if <paramref name="argument"/> is null.</summary>
        /// <param name="argument">The argument to validate as non-null.</param>
        /// <param name="paramName">The name of the parameter with which <paramref name="argument"/> corresponds.</param>
        public static void ThrowIfNull<T>([NotNull] T? argument, [CallerArgumentExpression("argument")] string? paramName = null)
            where T : struct;
    }
}

API Usage

public static int DereferenceSomePointer(int* pointer)
{
    ArgumentNullException.ThrowIfNull(pointer);
    return *pointer;
}

public static int DereferenceAnotherPointer(IntPtr pointer)
{
    ArgumentNullException.ThrowIfNull((void*)pointer);
    return *(int*)pointer;
}

public static int GetValueOfNullableInt(int? nullable)
{
    ArgumentNullException.ThrowIfNull(nullable);
    return nullable.Value;
}

[Flags]
public enum OperationFlags
{
    None       = 0,
    RequireInt = 1 << 0
}

public static void PerformOperation(OperationFlags flags, int? integer = null)
{
    if (flags.HasFlag(OperationFlags.RequireInt))
        ArgumentNullException.ThrowIfNull(integer);

    // perform operation
}

Risks

  • The Nullable<T> overload could potentially result in a lot of generic instantiations.
  • The pointer overload is an unsafe method being exposed on an extremely common safe type. I'm not sure how the .NET team feels about this.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions