jeudi 10 octobre 2019

Is there a way to get the name of the function argument (not the parameter)?

I am I am using C# 7+, and I am aware of the [CallerMemberName] attribute, but what I am looking for is an attribute that would get me the name of an argument.

Use case: Checking for null, even with the ?? and ? null operators, can be a bit tedious with the condition checks and throwing the proper exceptions with the proper values. For a few months now I've been using a solution inspired by some article I read and that could be described as an "argument validation builder". It would be used something like this:

public class MyClass
{
    public void DoTheThing(IFoo foo, ICollection<IBar> bars, string specialText)
    {
        new ArgumentValidator()
            .NotNull(foo, nameof(foo))
            .NotNullOrEmpty(bars, nameof(bars))
            .NotNullOrEmpty(specialText, nameof(specialText));

        ...rest of function
    }
}

If, for example, foo was null, then ArgumentValidator.NotNull(...) would throw a new ArgumentNullException with the parameter name "foo". This approach makes argument checking a bit more concise, and that's pretty much the only reason I'm doing this.

It would be really nice if I didn't have to specify nameof(...) every single time. That is, I'd like to be able to do this:

        new ArgumentValidator()
            .NotNull(foo)
            .NotNullOrEmpty(bars)
            .NotNullOrEmpty(specialText);

In order to do that though, I would need to figure how to make the NotNull(...) and other functions figure out the name of the argument.

I've tried making a parameter-based attribute, I've tried looking at Environment.StackTrace (not thrilled about trying to parse that nor about the performance implications), I've looked at StackFrame, I've looked at getting type info about the class -> method info -> parameter info and the custom attributes, and I still haven't found a way forward.

I'd like to make an attribute similar to [CallerMemberName], but this attribute would extract the name of the argument that was used to call the function, assign it to the decorated parameter, and would performs quickly (in other words, I want to avoid stack trace stuff if possible, especially since I'm using these checks a lot).

This is where I'm at:

[AttributeUsage( AttributeTargets.Parameter )]
class ArgumentNameAttribute : Attribute
{
    public string SomeProperty { get; set; }
}

class Program
{
    static void NotNull<T>( T argument, [ArgumentNameAttribute] string argumentName )
    {
        //how to get at the argumentName?
    }

    static void DoTheThing( string thing )
    {
        NotNull( thing );
        Console.WriteLine( "hello world" );
    }

    static void Main( string[] args )
    {
        DoTheThing( "12345" );
    }
}

Alternately, I'll accept another solution that makes argument checking concise and expressive.

Ideas?





Aucun commentaire:

Enregistrer un commentaire