Skip to content

Proposal: Convenience API for throwing exceptions #20604

@jamesqo

Description

@jamesqo

Background

It's quite verbose to validate arguments because you have to write an if-statement and throw new XXXException for each validation. Even with C# 7's new throw-expressions, you still have to write out the latter part. This makes people write their own helper classes for validating arguments, e.g. here or here. It would be nice if we had such a helper class as part of the framework.

Proposal

namespace System
{
    public static class Verify
    {
        public static void Argument(bool condition, string message, string argumentName);

        public static void InRange(bool condition, string argumentName);
        public static void InRange(string s, int index, int count, string sName, string indexName, string countName);
        public static void InRange<T>(T[] array, int index, int count, string arrayName, string indexName, string countName);

        public static void NotEmpty<T>(IEnumerable<T> source, string sourceName);

        public static void NotNegative(int argument, string argumentName);

        public static void NotNull(bool condition, string argumentName);
        public static void NotNull<T>(T argument, string argumentName) where T : class;

        public static void Positive(int argument, string argumentName);
    }
}

// Sample usage:
T MyGenericMethod<T>(T[] array, int index, int count)
{
    // Note: All of this is equivalent to Verify.InRange(array, index, count, nameof(array), nameof(index), nameof(count)).
    // The arguments are validated manually for demo purposes.
    Verify.NotNull(array, nameof(array));
    Verify.NotNegative(index, nameof(index));
    Verify.NotNegative(count, nameof(count));
    Verify.InRange(array.Length - index >= count, nameof(index));
}

A sample implementation can be found here.

Remarks

  • In my experience, it's common to validate things like a signed integer being positive/nonnegative, so those patterns will get their own Positive / NotNegative methods. This will enable us to provide a better error message if such a check fails. These methods throw the same exception type as InRange.

    • Same applies for InRange, NotEmpty
  • The class will work nicely with using static:

using static System.Verify;

T MyGenericMethod<T>(T[] array, int index, int count)
{
    NotNull(array, nameof(array));
    NotNegative(index, nameof(index));
    NotNegative(count, nameof(count));
    InRange(array.Length - index >= count, nameof(index));
}
  • The extra NotNull overload taking a bool covers the rare cases when people are writing generic code, and the parameter might be null but the compiler can't guarantee that it's a class. e.g. Like here. It also covers the rare times when someone would want to verify a nullable is non-null.
    • I didn't include a NotNull<T>(T?, string) where T : struct nullable overload since if someone thinks a nullable is non-null they would likely 1) not accept a nullable parameter in the first place, or 2) if they're calling some method they know returns a non-null nullable, they're likely to cast the T? to a T or call .Value, which do the validation automatically, instead of bothering to validate themselves.

This is a follow-up to https://github.com/dotnet/corefx/issues/12509 after putting some thought into the API.

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-needs-workAPI needs work before it is approved, it is NOT ready for implementationarea-System.Runtime

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions