Skip to content

[API Proposal]: ArgumentNullException.ThrowIfNull<T> #82227

@ghost

Description

Background and motivation

While working on a generic-class, that accepts both classes and structs, I noticed that 'ArgumentNullException.ThrowIfNull' performs a boxing-operation by converting the passed-variable to 'object?'.

The immediate notice is that all structs are boxed but, a seemingly unintended side-effect, is that 'struct?' is also boxed if its not null.

I would like to propose using generic-method functionality instead. This would shave the unnecessary boxing operations, for struct and 'struct?', which could be expensive.

API Proposal

namespace System;

public class ArgumentNullException : ArgumentException
{
    // SNIPPED

    public static void ThrowIfNull<T>([NotNull] T? argument, [CallerArgumentExpression("argument")] string? paramName = null)
    {
        if (argument is null)
        {
            Throw(paramName);
        }
    }

    // SNIPPED
}

API Usage

// Under the current-API, these will be boxed
int valueStruct = 25;
int? maybeNullStruct = 30;

// Under the current-API, this will not be boxed
int? definitelyNullStruct = null;

// When using the *new-API* ThrowIfNull<T>, none of these will be boxed
ArgumentNullException.ThrowIfNull(valueStruct);
ArgumentNullException.ThrowIfNull(maybeNullStruct);
ArgumentNullException.ThrowIfNull(definitelyNullStruct);

Alternative Designs

Creation of a separate-API, ArgumentNullException.ThrowGenericIfNull:

namespace System;

public class ArgumentNullException : ArgumentException
{
    // SNIPPED

    public static void ThrowGenericIfNull<T>([NotNull] T? argument, [CallerArgumentExpression("argument")] string? paramName = null)
    {
        if (argument is null)
        {
            Throw(paramName);
        }
    }

    // SNIPPED
}

Risks

Before making this issue, I looked extensively for resources showing the costs of generics and boxing; and could not find anything showing generic-methods being a worse option. However I also am not working directly on the runtime. Nor am I staring at C# every waking hour… Well I do actually, but you get what I mean.

There is the possibility of a performance-regression if my knowledge of the situation wasn’t sufficient.

I’ve also noticed that the runtime sparingly uses generic-methods in non-generic classes. This may be a styling-guideline not mentioned in the contribution-guide.

There is the possibility of a styling-conflict, that wasn’t explicitly mentioned prior.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions