Skip to content
This repository has been archived by the owner on Jul 18, 2023. It is now read-only.

Add support for 'CallerArgumentExpression' attribute #58

Closed
julealgon opened this issue Dec 24, 2020 · 9 comments
Closed

Add support for 'CallerArgumentExpression' attribute #58

julealgon opened this issue Dec 24, 2020 · 9 comments
Assignees
Milestone

Comments

@julealgon
Copy link

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).

@alexrp
Copy link

alexrp commented Jan 2, 2021

The attribute has been added to the BCL but it does not actually do anything yet: dotnet/csharplang#287

@julealgon
Copy link
Author

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.

@03l54rd1n3
Copy link

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

@patrikmolsson
Copy link

patrikmolsson commented Nov 28, 2021

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);
        }
    }
}

@julealgon
Copy link
Author

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.

@safakgur
Copy link
Owner

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 safakgur self-assigned this Dec 27, 2021
@safakgur safakgur added this to the 2.0 milestone Dec 27, 2021
@julealgon
Copy link
Author

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?

@sgf
Copy link

sgf commented Mar 9, 2023

@safakgur

@safakgur
Copy link
Owner

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.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants