在运行时解析参数名称

时间:2009-05-15 16:16:44

标签: c# reflection anonymous-types

  

可能重复:
  Finding the Variable Name passed to a Function in C#

在C#中,有没有办法(更好的方法)在运行时解析参数的名称?

例如,在以下方法中,如果重命名方法参数,则还必须记住更新传递给ArgumentNullException的字符串文字。

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

        // ..
    }

5 个答案:

答案 0 :(得分:26)

一种方式:

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

此代码还需要支持功能:

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

基本上,代码的工作原理是定义一个新的匿名类型,其中包含一个属性,该属性包含您想要的参数名称。 GetName()然后使用反射来提取该属性的名称。

此处有更多详细信息:http://abdullin.com/journal/2008/12/13/how-to-find-out-variable-or-parameter-name-in-c.html

答案 1 :(得分:15)

简短回答:不,没有。 (这简洁吗?;)

(编辑:贾斯汀的回答可能很重要。它在我的口中留下了不好的味道,但它实现了“不需要将参数名称放入字符串”的目标。我不认为我真的算AOP但是,因为这真的改变为一种完全不同的方法,而不是回答从方法中获取参数名称的原始问题。)

更长的答案:有一种方法可以找出方法的所有参数,但我不认为它在这种情况下有用。

这是一个显示来自几种方法的参数名称的示例:

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

这就是这样,但是如果你有多个参数而你想抛出一个合适的异常,你怎么知道(以安全的方式)使用哪个?理想情况下,您需要以下内容:

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

    // ..
}

神秘的infoof运算符会返回ParameterInfo。不幸的是,这不存在。

答案 2 :(得分:5)

我处理了同样的问题。获取参数名称有两种方法,但性能最佳的方法是深入了解IL。您可以在我的博客文章Taking the pain out of parameter validation上看到我的实施示例。

对此方法的一个警告是,您需要将参数名称作为委托传递,但为更清晰的代码付出的代价很小:

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

比以下更清洁,更清晰:

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

静态方法方法允许我在一个流畅的界面中将许多方法链接在一起。最初返回一个Argument对象,它只允许一个基本的null测试,它返回一个ReferenceArgument对象,然后可以进行额外的验证。如果被测对象是值类型,则可以使用不同的测试。

API允许进行许多常见测试,但很难捕获所有可能的测试,因此为了提供灵活性,通用测试方法允许提供表达式或函数,而在前者的情况下,表达式实际上可以用作错误信息。

我的示例仅涵盖了一些基础知识,但您可以轻松扩展接口以检查范围并抛出ArgumentOutOfRangeExceptions或从特定基类继承的测试对象或实现接口。有一些类似的实现,但我还没有看到任何获取参数名称。

答案 3 :(得分:3)

您可以使用AOP获取此信息。您可以定义在方法执行之前调用的拦截,并在那里抛出异常。这也解决了空检查是一个贯穿各领域的问题。

PostSharp是AOP的简单实现。

这是你的代码看起来像什么(没有经过测试,但它应该让你非常接近)

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

现在,您可以修改方法:

[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
}

答案 4 :(得分:0)

你可能想要:

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

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

并称之为:

Class.ThrowIfNull(() => resource);

但这不是你想要的。它也慢很多1)比2)慢1000倍。可能是:

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

并称之为:

new { resource }.ThrowIfNull();

清洁剂,比2以上快得多! :)

您还可以为对象的属性扩展这些方法。例如,

new { myClass.MyProperty1 }.ThrowIfNull();

您可以缓存属性值以进一步提高性能,因为属性名称在运行时不会更改。请参阅相关问题Finding the variable name passed to a function