Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[API Proposal]: ArgumentException.ThrowIfNullOrEmpty(string) #62628

Closed
datvm opened this issue Dec 10, 2021 · 24 comments · Fixed by #64357
Closed

[API Proposal]: ArgumentException.ThrowIfNullOrEmpty(string) #62628

datvm opened this issue Dec 10, 2021 · 24 comments · Fixed by #64357
Assignees
Labels
api-approved API was approved in API review, it can be implemented area-System.Runtime
Milestone

Comments

@datvm
Copy link

datvm commented Dec 10, 2021

Background and motivation

Inspired by ArgumentNullException.ThrowIfNull(object). string.IsNullOrEmpty(text) is a frequent check especially in web/Console project. At first I thought InvalidDataException would be the best name but it is in System.IO namespace, so I think the closest should be ArgumentException so user doesn't need to import that namespace.

Now:

if (string.IsNullOrEmpty(input)) { throw new ArgumentException($"{nameof(input)} must contain value."); }

If the API is approved:

ArgumentException.ThrowIfNullOrEmpty(input);

While we are at it, personally I rarely use it but maybe ThrowIfNullOrWhiteSpace may be useful for others as well?

API Proposal

namespace System
{
    public class ArgumentException : SystemException
    {
        public static void ThrowIfNullOrEmpty([NotNull] string? argument, [CallerArgumentExpression("argument")] string? paramName = null)
    }
}

API Usage

ArgumentException.ThrowIfNullOrEmpty(input);

Alternative Designs

No response

Risks

No response

@datvm datvm added the api-suggestion Early API idea and discussion, it is NOT ready for implementation label Dec 10, 2021
@dotnet-issue-labeler
Copy link

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

@dotnet-issue-labeler dotnet-issue-labeler bot added the untriaged New issue has not been triaged by the area owner label Dec 10, 2021
@ghost
Copy link

ghost commented Dec 10, 2021

Tagging subscribers to this area: @dotnet/area-system-runtime
See info in area-owners.md if you want to be subscribed.

Issue Details

Background and motivation

Inspired by ArgumentNullException.ThrowIfNull(object). string.IsNullOrEmpty(text) is a frequent check especially in web/Console project. At first I thought InvalidDataException would be the best name but it is in System.IO namespace, so I think the closest should be ArgumentException so user doesn't need to import that namespace.

Now:

if (string.IsNullOrEmpty(input)) { throw new ArgumentException($"{nameof(input)} must contain value."); }

If the API is approved:

ArgumentException.ThrowIfNullOrEmpty(input);

While we are at it, personally I rarely use it but maybe ThrowIfNullOrWhiteSpace may be useful for others as well?

API Proposal

namespace System
{
    public class ArgumentException : SystemException
    {
        public static void ThrowIfNullOrEmpty([NotNull] string argument, [CallerArgumentExpression("argument")] string? paramName = null)
    }
}

API Usage

ArgumentException.ThrowIfNullOrEmpty(input);

Alternative Designs

No response

Risks

No response

Author: datvm
Assignees: -
Labels:

api-suggestion, area-System.Runtime, untriaged

Milestone: -

@Clockwork-Muse
Copy link
Contributor

[NotNull]

... but the parameter can be null, since that's the point of the exception?

@stephentoub
Copy link
Member

stephentoub commented Dec 10, 2021

The argument to the existing ThrowIfNull is declared as:

[NotNull] object? argument

which uses ? to avoid warnings when being used with something that may be null, but then [NotNull] to say "upon successful return from this method, it's guaranteed argument isn't null."

@danmoseley danmoseley added api-ready-for-review API is ready for review, it is NOT ready for implementation and removed api-suggestion Early API idea and discussion, it is NOT ready for implementation labels Dec 14, 2021
@danmoseley
Copy link
Member

danmoseley commented Dec 14, 2021

Makes sense to me. It would collapse stuff like

if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
if (name.Length == 0)
{
throw new ArgumentException(SR.Argument_EmptyName, nameof(name));
}

from 10 lines to 1.

In that example, it has a custom message, ie "Empty name is not legal.", and using this proposed API would change that to something generic. I think that's totally fine. There are cases where perhaps the message adds slightly more value eg "Empty file name is not legal." and perhaps those would not change to use this API, which is fine.

@hrrrrustic
Copy link
Contributor

hrrrrustic commented Dec 15, 2021

Maybe it would also make sense to add overload with IReadOnlyCollection<T> or simply ICollection<T>?

public static void ThrowIfNullOrEmpty<T>([NotNull] I/*ReadOnly*/Collection<T> argument, [CallerArgumentExpression("argument")] string? paramName = null);

Usages:

if (source == null || source.Length == 0)
throw new ArgumentException(SR.Format(SR.MissingParameter, nameof(source)));

if (rawData == null || rawData.Length == 0)
throw new ArgumentException(SR.Arg_EmptyOrNullArray, nameof(rawData));

Didn't try to find splitted if-checks

Makes sense to me. It would collapse stuff like

if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
if (name.Length == 0)
{
throw new ArgumentException(SR.Argument_EmptyName, nameof(name));
}

from 10 lines to 1.

In that example, it has a custom message, ie "Empty name is not legal.", and using this proposed API would change that to something generic. I think that's totally fine. There are cases where perhaps the message adds slightly more value eg "Empty file name is not legal." and perhaps those would not change to use this API, which is fine.

@Rubenisme
Copy link

Rubenisme commented Dec 23, 2021

If this is done, then after that I'm sure someone will ask: "What about an ArgumentException.ThrowIfNullOrWhiteSpace?".
I think the string.IsNullOrWhiteSpace method gets used a lot less than the string.IsNullOrEmpty method, so might not be as useful.

@terrajobst
Copy link
Member

terrajobst commented Jan 4, 2022

Video

  • Looks good as proposed
    • Might be good to do a pass what ArgumentOutOfRangeException cases we use internally. We could base it off the unique resource strings we use in corlib.
  • The API should
    • Throw ArgumentNullException if argument is null, otherwise ArgumentException
  • We don't believe we need an overload of ThrowIfNullOrEmpty for collections as this feels rare.
namespace System
{
    public partial class ArgumentException
    {
        public static void ThrowIfNullOrEmpty([NotNull] string? argument,
                                              [CallerArgumentExpression("argument")] string? paramName = null);
    }
}

@terrajobst terrajobst added api-approved API was approved in API review, it can be implemented and removed api-ready-for-review API is ready for review, it is NOT ready for implementation labels Jan 4, 2022
@hrrrrustic
Copy link
Contributor

Guys check suggestion about ICollection above please :)

@terrajobst
Copy link
Member

@hrrrrustic I edited my comment.

@danmoseley
Copy link
Member

@datvm do you want to add the API now?

@datvm
Copy link
Author

datvm commented Jan 4, 2022

@danmoseley awesome, please go ahead

@deeprobin
Copy link
Contributor

To conform to the string overloads, it would also make sense to create an overload for IsNullOrWhiteSpace.

namespace System
{
    public partial class ArgumentException
    {
        public static void ThrowIfNullOrEmpty([NotNull] string? argument,
                                              [CallerArgumentExpression("argument")] string? paramName = null);
        public static void ThrowIfNullOrWhiteSpace([NotNull] string? argument,
                                              [CallerArgumentExpression("argument")] string? paramName = null);
    }
}

@terrajobst
Copy link
Member

Do you find yourself ever checking against whitespace only?

@deeprobin
Copy link
Contributor

Only against whitespace actually not. But checking against null or whitespace is a common practice.
This includes partial argument parsing of commands (if you don't want a command to be trimmed, for example).

Even if there are not many use cases for this case with me, I think imo it would be good to add this case anyway for completeness.

@datvm
Copy link
Author

datvm commented Jan 11, 2022

add this case anyway for completeness.

I wonder what's the API designer team philosophy on this. From the video I watched, it seems they prefer to have only very needed APIs added instead of "completeness". I understand and agree with this approach since .NET now can be used in Web environment through WebAssembly as well so they probably want the standard library to be as small as possible. You should check the above video, they discussed about the possibilities of adding other throw helpers as well.

@deeprobin
Copy link
Contributor

Makes sense to me. It would collapse stuff like

if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
if (name.Length == 0)
{
throw new ArgumentException(SR.Argument_EmptyName, nameof(name));
}

from 10 lines to 1.

In that example, it has a custom message, ie "Empty name is not legal.", and using this proposed API would change that to something generic. I think that's totally fine. There are cases where perhaps the message adds slightly more value eg "Empty file name is not legal." and perhaps those would not change to use this API, which is fine.

But that would change the exception behavior, since an ArgumentException would be thrown instead of an ArgumentNullException?

Or should the proposal throw an ArgumentNullException? In that case it would make sense to move it to the ArgumentNullException class?

@deeprobin
Copy link
Contributor

deeprobin commented Jan 11, 2022

add this case anyway for completeness.

I wonder what's the API designer team philosophy on this. From the video I watched, it seems they prefer to have only very needed APIs added instead of "completeness". I understand and agree with this approach since .NET now can be used in Web environment through WebAssembly as well so they probably want the standard library to be as small as possible. You should check the above video, they discussed about the possibilities of adding other throw helpers as well.

You're right about that.
For NativeAOT we could consider that isn't it? Or are unused methods not optimized away?

@danmoseley
Copy link
Member

Or should the proposal throw an ArgumentNullException? In that case it would make sense to move it to the ArgumentNullException class?

As noted above, it should throw ANE (which is-a AE) if the argument is null.

@danmoseley
Copy link
Member

@danmoseley awesome, please go ahead

@datvm I meant, are you interested in making the PR? No obligation, it's just that you opened the issue so asking you first. If anyone else is interested, they're welcome too.

We have full instructions for how to contribute and can help as much as you need.

@datvm
Copy link
Author

datvm commented Jan 11, 2022

@danmoseley ah thanks for clarifying. I will leave this one for someone else. I will try next time when I have studied an example pull request.

@danmoseley
Copy link
Member

@datvm sounds good, if you are interested there are issues with the 'easy' label and perhaps there's one you might be interested to start with.

Anyone else interested in offering a PR here?

@deeprobin
Copy link
Contributor

@datvm sounds good, if you are interested there are issues with the 'easy' label and perhaps there's one you might be interested to start with.

Anyone else interested in offering a PR here?

Doesn't seem like a big deal. If it's nothing urgent, I'm happy to do it.

@stephentoub stephentoub self-assigned this Jan 25, 2022
@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Jan 26, 2022
@stephentoub
Copy link
Member

Related: dotnet/roslyn#59094

@adamsitnik adamsitnik removed the untriaged New issue has not been triaged by the area owner label Jan 27, 2022
@adamsitnik adamsitnik added this to the 7.0.0 milestone Jan 27, 2022
@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Jan 28, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Feb 28, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-approved API was approved in API review, it can be implemented area-System.Runtime
Projects
None yet
Development

Successfully merging a pull request may close this issue.

10 participants