C# 在运行时解析参数名称

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/869610/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 04:55:18  来源:igfitidea点击:

Resolving a parameter name at runtime

c#reflectionanonymous-types

提问by xyz

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

可能的重复:
在 C# 中查找传递给函数的变量名

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

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

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.

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

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

        // ..
    }

采纳答案by Justin Ethier

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.

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

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

这里有更多细节:http: //abdullin.com/journal/2008/12/13/how-to-find-out-variable-or-parameter-name-in-c.html

回答by Jon Skeet

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.)

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

Longer answer: There's a way to find out all the parametersof 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 infoofoperator would return a ParameterInfo. Unfortunately this doesn't exist.

神话般的infoof运算符将返回一个ParameterInfo. 不幸的是,这不存在。

回答by Michael Meadows

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.

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

PostSharpis a good simple implementation of AOP.

PostSharp是 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
}

回答by Bronumski

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.

我处理了同样的问题。有几种获取参数名称的方法,但最有效的是深入到 IL 中。你可以在我关于这个问题的博客文章中看到我的实现示例 解决参数验证的痛苦

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.

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

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.

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

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.

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

回答by nawfal

You might want:

你可能想要:

1)

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)

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:

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

3)

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! :)

更干净,比上面的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

您可以缓存属性值以进一步提高性能,因为属性名称在运行时不会更改。查看相关问题查找传递给函数的变量名