InBetween
InBetween

Reputation: 32760

Getting the callee's name of an extension method

I've built up a simple ArgumentValidator class in order to simplify argument preconditions in any given method. Most of them are null or bounds checks and it gets pretty tedious after a couple of

if (arg == null ) throw new ArgumentNullException(nameof(arg));

So I've come up with the following set up:

public static class ArgumentValidator
{
    public interface IArgument<T>
    {
        string ParamName { get; }
        T Value { get; }
    }

    private class Argument<T>: IArgument<T>
    {
        public Argument(T argument, string paramName)
        {
            ParamName = paramName;
            Value = argument;
        }

        public string ParamName { get; }
        public T Value { get; }
    }

    public static IArgument<T> Validate<T>(this T argument, string paramName = null)
    {
        return new Argument<T>(argument, paramName ?? string.Empty);
    }

    public static IArgument<T> IsNotNull<T>(this IArgument<T> o)
    {
        if (ReferenceEquals(o.Value, null))
            throw new ArgumentNullException(o.ParamName);

        return o;
    }

    public static IArgument<T> IsSmallerThan<T, Q>(this IArgument<T> o, Q upperBound) where T : IComparable<Q> { ... }

    //etc.
}

And I can use it in the following way:

public Bar Foo(object flob)
{
     flob.Validate(nameof(flob)).IsNotNull().IsSmallerThan(flob.MaxValue);
}

Ideally I'd love to get rid of nameof(flob) in the Validate call and ultimately get rid of Validate alltogether; the only purpose of Validate is to avoid having to pass nameof(...) on every check down the chain.

Is there a way to get the name flob inside the Validate() method?

Upvotes: 4

Views: 1719

Answers (1)

Patrick Hofman
Patrick Hofman

Reputation: 156978

Doing that with an extension method is not that easy. It is easier with a static method that takes an LINQ expression (derived from devdigital's answer here):

public static T Validate<T>(this Expression<Func<T>> argument)
{
    var lambda = (LambdaExpression)argument;

    MemberExpression memberExpression;
    if (lambda.Body is UnaryExpression)
    {
        var unaryExpression = (UnaryExpression)lambda.Body;
        memberExpression = (MemberExpression)unaryExpression.Operand;
    }
    else
    {
        memberExpression = (MemberExpression)lambda.Body;
    }

    string name = memberExpression.Member.Name;

    Delegate d = lambda.Compile();

    return (T)d.DynamicInvoke();
}

The name inside is the name of the property you put in the method:

MyMethods.Validate(() => o);

Since the Validate returns T, you can use that further on. This might not be as performing as you want it to be, but this is the only viable option.

It is possible to make this an extension method too, you have to create the expression yourself by hand:

Expression<Func<object>> f = () => o; // replace 'object' with your own type

f.Validate();

Upvotes: 5

Related Questions