20

Possible Duplicate:
Finding the Variable Name passed to a Function in C#

In C#, is there a way (terser the better) to resolve the name of a parameter at runtime?

For example, in the following method, if you renamed the method parameter, you'd also have to remember to update the string literal passed to ArgumentNullException.

    public void Woof(object resource)
    {
        if (resource == null)
        {
            throw new ArgumentNullException("resource");
        }

        // ..
    }
Community
  • 1
  • 1
xyz
  • 27,223
  • 29
  • 105
  • 125
  • 1
    I think that what you truly want is a tool such as re-sharper or a custom StyleCop rule which can detect this particular mismatch. If it is an ArgumentNullException, then it must use a variable name that is a parameter name. – Hamish Grubijan Jun 10 '10 at 18:21
  • @nawfal: That one refers to a different kind of variable name. This is referring to the parameter, not the variable passed in. – Guvante Feb 03 '13 at 20:19
  • @Guvante the solution/strategy are all the same. They are just about variables no matter where you declare them. – nawfal Feb 03 '13 at 20:21
  • 3
    I can't post an answer because this question has been (wrongly) marked as duplicate. In C# 6.0 you will be able to use the [`nameof` operator](https://roslyn.codeplex.com/discussions/570551) for that: `if (resource == null) { throw new ArgumentNullException(nameof(resource));}` – Paolo Moretti Oct 29 '14 at 12:03

5 Answers5

26

One way:

static void Main(string[] args)
{
  Console.WriteLine("Name is '{0}'", GetName(new {args}));
  Console.ReadLine();
}

This code also requires a supporting function:

static string GetName<T>(T item) where T : class
{
  var properties = typeof(T).GetProperties();
  Enforce.That(properties.Length == 1);
  return properties[0].Name;
}

Basically the code works by defining a new Anonymous Type with a single Property consisting of the parameter who's name you want. GetName() then uses reflection to extract the name of that Property.

There are more details here: http://abdullin.com/journal/2008/12/13/how-to-find-out-variable-or-parameter-name-in-c.html

Justin Ethier
  • 131,333
  • 52
  • 229
  • 284
  • 5
    Eewww - and at the same time, neat! I really can't decide whether this counts as a hideous abuse of anonymous types or not. – Jon Skeet May 15 '09 at 16:29
  • 1
    Justin: I strongly suggest you provide a fuller explanation in this answer. Currently it doesn't show anything about how GetName works. The idea in this answer definitely deserves plenty of votes - but could do with being fleshed out. – Jon Skeet May 15 '09 at 16:33
  • Clever, and it all appears to be supported by the C# spec too, so far as I can tell. – LukeH May 15 '09 at 17:12
  • ah ha! what an interesting way around the problem. suck on that reflection namespace! – andy Apr 04 '10 at 22:45
  • 1
    Just because you can do this, doesn't mean you should. This adds so much more complexity to something that isn't too much of a burden already. Right-clicking and "Refactor" takes care of the original request. Being this "clever" will makes this code much more difficult to maintain - I respectfully vote against this. – Robert Seder Jun 28 '10 at 23:58
  • 2
    @Robert Seder Right-click-Refactor won't change the string literal; that was the motivation for the question. – xyz Dec 30 '10 at 11:59
  • Refactor doesnt work with this. new { x } --> refactory to y, produces new { x = y } – apocalypse Mar 23 '15 at 12:05
15

Short answer: No, there isn't. (Is that terse enough? ;)

(EDIT: Justin's answer probably counts. It leaves a bad taste in my mouth, but it accomplishes the goal of "no need to put the parameter name into a string". I don't think I'd really count AOP though, as that's really changing to a completely different approach rather than answering the original question of getting a parameter name from within a method.)

Longer answer: There's a way to find out all the parameters of a method, but I don't think it's useful in this case.

Here's an example which displays the parameter names from a couple of methods:

using System;
using System.Reflection;

class Test
{
    static void Main()
    {
        Foo(null);
        Bar(null);
    }

    static void Foo(object resource)
    {
        PrintParameters(MethodBase.GetCurrentMethod());
    }

    static void Bar(object other)
    {
        PrintParameters(MethodBase.GetCurrentMethod());
    }

    static void PrintParameters(MethodBase method)
    {
        Console.WriteLine("{0}:", method.Name);
        foreach (ParameterInfo parameter in method.GetParameters())
        {
            Console.WriteLine(" {0} {1}",
                              parameter.ParameterType,
                              parameter.Name);
        }
    }
}

So that does that, but if you have multiple parameters and you wanted to throw an appropriate exception, how would you know (in a safe way) which to use? Ideally you want something like:

public void Woof(object resource)
{
    if (resource == null)
    {
        throw new ArgumentNullException(infoof(resource));
    }

    // ..
}

where the mythical infoof operator would return a ParameterInfo. Unfortunately this doesn't exist.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Thanks for the thorough answer. – xyz May 16 '09 at 09:04
  • @RobH: I don't *remember* doing that then... you can do it with an anonymous type, although depending on how you refactor that can be dangerous too. Is that what you were thinking of? – Jon Skeet Jul 23 '13 at 16:49
5

I dealt with this very same issue. There are a couple of ways of getting the parameter name but the most performant is to dip down into the IL. You can see an example of my implementation on my blog post on this very issue Taking the pain out of parameter validation.

The one caveat to this approach is you need to pass the parameter name in as a delegate but it is small price to pay for cleaner code:

public void SomeMethod(string value)
{
    Validate.Argument(() => value).IsNotNull().IsNotEmpty();
}

Which is somewhat cleaner and clearer than:

public void SomeMethod(string value)
{
    if (value == null)
    {
        throw new ArgumentNullException("value");
    }
    if (value == string.Empty)
    {
        throw new ArgumentException("Value cannot be an empty string.", "value");
    }
}

The static method approach has allowed me to chain a number of methods together in a fluent interface. Initially an Argument object is returned which only allows a basic null test which returns a ReferenceArgument object which can then have additional validation. If the object under test is a value type then different tests are available.

The API allows for a number of common tests but it would be hard to capture all the possible tests so to provide flexibility a generic test method allows an expression or function to be provided and in the case of the former the expression can actually be used as the error message.

My example only covers a few of the basics but you can easily expand the interface to check for ranges and throw ArgumentOutOfRangeExceptions or test objects inherit from a specific base class or implement an interface. There are some similar implementations but I have not as yet seen any that get the parameter name.

Bronumski
  • 14,009
  • 6
  • 49
  • 77
  • I would argue that most developers would scratch their head that that. To me, this crosses the line of being so clever, it's not maintainable. What exactly is so difficult about the extremely common TWO lines of code (compared to your ONE): public void SomeMethod(string value) { if (string.IsNullOrEmpty(value)) throw new ArgumentException("Argument 'value' cannot be null or empty."); } I may be the only one, but I dissent on this approach! – Robert Seder Jun 29 '10 at 00:07
  • 3
    The issue is that the out of the box approach is over bloated, it takes away from what the method is trying to do by distracting you. Additionally there is a maintenance issue of first putting in the parameter name that is throwing the exception and second maintaining its name when the parameter name changes. All I have done is move this logic into an easy to use fluent interface that comes with less (but still present) clutter. If this type of approach was not need then there wouldn't be so many blog posts pushing their Guard APIs or the Microsoft code contracts for .net 4.0. – Bronumski Jul 05 '10 at 16:29
3

You can get this information using AOP. You can define an intercept that is invoked before method execution and throw the exception there. This also takes care of the problem that null checking is a cross-cutting concern.

PostSharp is a good simple implementation of AOP.

Here's what your code would look like (haven't tested, but it should get you very close)

[AttributeUsage(AttributeTargets.Parameter)]
public class CanBeNullAttribute : Attribute
{
    private readonly bool canBeNull;

    public CanBeNullAttribute()
        : this(true)
    {
    }

    public CanBeNullAttribute(bool canBeNull)
    {
        this.canBeNull = canBeNull;
    }

    public bool AllowNull
    {
        get { return canBeNull; }
    }
}

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class EnforceNullConstraintAttribute : OnMethodInvocationAspect
{
    public override void OnInvocation(MethodInvocationEventArgs eventArgs)
    {
        object[] arguments = eventArgs.GetArgumentArray();
        ParameterInfo[] parameters = eventArgs.Delegate.Method.GetParameters();

        for (int i = 0; i < arguments.Length; i++)
        {
            if (arguments[i] != null) continue;

            foreach (CanBeNullAttribute attribute in parameters[i].GetCustomAttributes(typeof(CanBeNullAttribute), true))
            {
                if (!attribute.AllowNull) throw new ArgumentNullException(parameters[i].Name);
            }
        }

        base.OnInvocation(eventArgs);
    }
}

Now, you can modify your method:

[EnforceNullConstraint]
public void Woof([CanBeNull(false)] object resource)
{
    // no need to check for null, PostSharp will weave it at compile time

    // execute logic assured that "resource" is not null
}
Michael Meadows
  • 27,796
  • 4
  • 47
  • 63
  • I don't think I'd use it in this case, but that is a very interesting approach for the toolbox. Thanks for bringing it up! – xyz May 16 '09 at 09:09
0

You might want:

1)

public static void ThrowIfNull<T>(Expression<Func<T>> expr)
{
    if (expr == null || expr.Compile()() != null) //the compile part is slow
        return;

    throw new ArgumentNullException(((MemberExpression)expr.Body).Member.Name);
}

or

2)

public static void ThrowIfNull<T>(Expression<Func<T>> expr)
{
    if (expr == null)
        return;

    var param = (MemberExpression)expr.Body;
    if (((FieldInfo)param.Member).GetValue(((ConstantExpression)param.Expression).Value) == null)
        throw new ArgumentNullException(param.Member.Name);
}

And call it:

Class.ThrowIfNull(() => resource);

But that's not what you would want probably. Its also a lot slower 1) is abt 1000 times slower than 2). May be:

3)

public static void ThrowIfNull<T>(this T item) where T : class
{
    if (item == null)
        return;

    var param = typeof(T).GetProperties()[0];
    if (param.GetValue(item, null) == null)
        throw new ArgumentNullException(param.Name);
}

And call it:

new { resource }.ThrowIfNull();

Cleaner, much faster than above 2! :)

You can also extend these methods for properties of objects. For eg.,

new { myClass.MyProperty1 }.ThrowIfNull();

You can cache property values to improve performance further as property names don't change during runtime. See related question Finding the variable name passed to a function

Community
  • 1
  • 1
nawfal
  • 70,104
  • 56
  • 326
  • 368