Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Resolving a parameter name at runtime [duplicate]

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

        // ..
    }
like image 370
xyz Avatar asked May 15 '09 16:05

xyz


4 Answers

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

like image 172
Justin Ethier Avatar answered Nov 01 '22 16:11

Justin Ethier


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.

like image 31
Jon Skeet Avatar answered Nov 01 '22 16:11

Jon Skeet


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.

like image 5
Bronumski Avatar answered Nov 01 '22 18:11

Bronumski


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
}
like image 3
Michael Meadows Avatar answered Nov 01 '22 16:11

Michael Meadows