guard icon indicating copy to clipboard operation
guard copied to clipboard

Add support for 'CallerArgumentExpression' attribute

Open julealgon opened this issue 4 years ago • 7 comments

Is your feature request related to a problem? The (value, nameof(value)) way of specifying the argument name is not necessary anymore when using C#8, since the CallerArgumentExpressionAttribute has been added to capture the name of any argument passed into the method.

Describe the solution you'd like The Argument method should rely on [CallerArgumentExpression] to fetch the argument name automatically without having to pass it from the caller. This would considerably clean up the usage of the library while also discouraging the use of the Expression-based approach which is very slow.

I'm actually quite surprised that this hasn't been implemented yet after reading all the documentation. If authors are aware of such attribute, I'd like to read the reasons why this hasn't been put in place yet (besides being unavailable in older runtimes).

julealgon avatar Dec 24 '20 15:12 julealgon

The attribute has been added to the BCL but it does not actually do anything yet: https://github.com/dotnet/csharplang/issues/287

alexrp avatar Jan 02 '21 05:01 alexrp

Oh wow! I was one of those who got misled by the documentation. Had no idea this was not yet supported.

Thanks @alexrp for clarifying.

I wonder though, would it make sense to add the needed code to support it right now, so that once it starts working there is no extra work to do? Since they have already published the attribute it's unlikely it will suffer any further breaking changes.

julealgon avatar Jan 02 '21 16:01 julealgon

Unlikely may not mean certain. Still, certain as of a few days ago this attribute will be available starting from C# 10 onwards. See dotnet/roslyn/pull/54839

03l54rd1n3 avatar Jul 27 '21 14:07 03l54rd1n3

In the meantime - in order to utilize this functionality - you can make a small abstraction on top of this library in your own code:

public static class MyGuard
{
    public static Dawn.Guard.ArgumentInfo<T> Argument<T>(T argument, [CallerArgumentExpression("argument")] string? argumentName = default)
    {
        return Dawn.Guard.Argument(argument, argumentName);
    }
}
public class GuardTests
{
    [Fact]
    public void SourcesMemberName()
    {
        // Arrange
        var expectedErrorMessage = $"year cannot be less than 0. (Parameter 'year'){Environment.NewLine}Actual value was -1.";

        // Act
        var ex = Assert.Throws<ArgumentOutOfRangeException>(() => new Example(-1));

        // Assert
        Assert.Equal(expectedErrorMessage, ex.Message);
    }

    private class Example
    {
        public Example(int year)
        {
            MyGuard.Argument(year).Min(0);
        }
    }
}

patrikmolsson avatar Nov 28 '21 11:11 patrikmolsson

In the meantime

@safakgur now that C#10 has officially launched, do you know when support for CallerArgumentExpression will be in place? I'm actually planning a migration to Guard in a sizeable solution soon and it would be really nice to have this supported by then as it would remove the need for an additional cleanup pass in all the code removing the parameter name argument later.

julealgon avatar Nov 29 '21 15:11 julealgon

Sorry everyone, I neglected Guard a lot during the last year because of other responsibilities (the pandemic definitely didn't help). But I'm still working on v2, our next major version which I hope to release before March.

@julealgon - Yes, it's coming with v2! The new version will also also have some generic validations that accept a bool condition marked with this attribute, so both the parameter name and the message containing the failed condition can be retrieved automatically (Think of Argument(age).Range(age >= 18) throwing an ArgumentOutOfRangeException with the param name "age" and the message "Precondition failed: age >= 18").

With that said, v2 will include a lot of other changes, some of which will be breaking changes, so you may not want to migrate a sizeable solution to use Guard before that.

I hope that helps!

safakgur avatar Dec 27 '21 14:12 safakgur

I'm still working on v2, our next major version which I hope to release before March.

@safakgur do you have any updates to share on v2?

julealgon avatar Mar 07 '22 21:03 julealgon

@safakgur

sgf avatar Mar 09 '23 02:03 sgf

Hi folks, this was one of the things I wanted for v2 but I'm retiring Guard - I apologise for any inconvenience, and thank you for all the support. The readme has details.

safakgur avatar Jul 18 '23 04:07 safakgur