Skip to content

[API Proposal]: ArgumentOutOfRangeException.ThrowIfEqual and ThrowIfNotEqual #82667

Description

@Sergio0694

Background and motivation

As #69590 added new throw helpers for ArgumentOutOfRangeException, I've started playing around with them trying to move an existing codebase to these new APIs, and one thing that quickly became apparent was how the "equal/not equal" case was currently missing. This proposal is about adding such APIs as well, so that these scenarios can also be supported.

API Proposal

namespace System;

public class ArgumentOutOfRangeException
{
    public static void ThrowIfEqual<T>(T value, T other, [CallerArgumentExpression("value")] string? paramName = null) where T : IEquatable<T>;
    public static void ThrowIfNotEqual<T>(T value, T other, [CallerArgumentExpression("value")] string? paramName = null) where T : IEquatable<T>;
}

I've tried to mirror the design of existing APIs such as ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual, which also take a non-nullable generic, constrained to an interface (in that case it's IComparable<T>).

API Usage

Eg. in ComputeSharp I have a bunch of methods to copy to/from MD textures. Consider this one, using this API:

public static void CopyTo<T>(this Texture2D<T> source, T[,] destination)
    where T : unmanaged
{
    ArgumentNullException.ThrowIfNull(source);
    ArgumentNullException.ThrowIfNull(destination);
    ArgumentOutOfRangeException.ThrowIfNotEqual(destination.GetLength(0), source.Height, nameof(destination));
    ArgumentOutOfRangeException.ThrowIfNotEqual(destination.GetLength(1), source.Width, nameof(destination));

    source.CopyTo(ref destination[0, 0], destination.Length, 0, 0, source.Width, source.Height);
}

In general this would be useful whenever people need to explicitly check against a single value (same as eg. ThrowIfZero).

Open questions

The IComparable<T> constraint means Enum values can't use this API. Should we not use the constraint and instead just have the API call EqualityComparer<T>.Default.Equals instead? Are there performance considerations in that case?

Metadata

Metadata

Assignees

Labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions