通过反射获取属性名称而无需传递它?

3

我想要做这样的事情:

    public static class Validate
    {
        public static void AgainstNull(string str)
        {
            if (String.IsNullOrWhiteSpace(str))
            {
                // how do I know the property name in the calling code?
                throw new ArgumentNullException("property name from caller");
            }
        }
    }

这样我就可以在我的代码库中使用类似于这样的模式:

    public void Foo(string bar)
    {
        Validate.AgainstNull(bar);

        // other processing here
    }

如何在我的验证方法中获取调用代码传入的属性名称?


理论上,您可以使用类似于 Validate.AgainstNull(() => bar) 的LINQ表达式,但我不确定实际编译、调用和检查这些表达式所带来的性能损失是否值得。在过去,我曾经简单地传递要检查的对象及其名称作为字符串进行操作。您考虑过使用代码契约吗? - Chris Sinclair
可能是重复问题:https://dev59.com/uVHTa4cB1Zd3GeqPTKPX - Jeff B
1
错误的问题。你怎么知道传递给Foo()的属性的名称?然后,你怎么知道调用Foo()的Bar()传递的属性的名称?接下来是Baz,无限循环。当然,这是行不通的,这就是为什么NRE的工作方式是这样的。 - Hans Passant
在你的例子中,你想要返回 Foo(它不是一个“属性”)还是调用 Foo 的属性? - D Stanley
4个回答

3

正如Chris Sinclair所提到的,您可以使用LINQ表达式,以下是这种代码的示例:

public static class Validate
{
    public static void AgainstNull(System.Linq.Expressions.Expression<Func<string>> expr)
    {
        var str = expr.Compile().Invoke();
        if (str == null)
        {
            string name = (expr.Body as System.Linq.Expressions.MemberExpression).Member.Name;
            throw new ArgumentNullException(name);
        }
    }
}

你会怎么称呼它? - Jeff B
@JeffBridgman:Validate.AgainstNull(() => bar);(虽然在这种情况下,该方法可能会更新为使用通用的 T 而不是 string)。 - Chris Sinclair

2

虽然直接获取参数名称不可能,但是有一种技巧/黑客方法可以通过将它们作为匿名类型的成员来检索参数名称。

根据您的例子,这并适合。它引入了不必要的歧义,并需要弱类型的方法签名。它还比仅传递所需参数的字符串名称慢得可衡量。

请再次注意,不要将其用于所述目的。

代码

void Main()
{
    Foo( "hello", "world", 123, false );
}

private static void Foo( string bar, string baz, int abc, bool xyz )
{
    Evaluate( new { bar, baz, abc, xyz } );
}

private static void Evaluate( object o )
{
    var properties = System.ComponentModel.TypeDescriptor.GetProperties( o );
    foreach( System.ComponentModel.PropertyDescriptor propertyDescriptor in properties )
    {
        var value = propertyDescriptor.GetValue( o );
        Console.WriteLine( "Name: {0}, Value: {1}", propertyDescriptor.Name, value );
    }
}

输出

Name: bar, Value: hello
Name: baz, Value: world
Name: abc, Value: 123
Name: xyz, Value: False

什么时候使用这种模式比较合适?

值得注意的是,ASP.Net MVC框架广泛地使用匿名类型作为一种语法快捷方式。 ComponentModel 代码直接来自于RouteValueDictionary


1
简单的回答:你不能。
在较新版本的.NET中有一些属性,我认为它们会很有帮助,但它们似乎也无法解决这个问题。

0
您可以使用表达式树来获取参数的名称。
    public static class Validate
    {
        public static void AgainstNull(string str)
        {
            if (String.IsNullOrWhiteSpace(str))
            {
                var parametersNames = GetParameterNames(() => AgainstNull(str));
                throw new ArgumentNullException(parametersNames[0]);
            }
        }

        private static string[] GetParameterNames(Expression<Action> expression)
        {
            var methodInfo = ((MethodCallExpression)expression.Body).Method;
            var names = methodInfo.GetParameters().Select(p => p.Name);
            return names.ToArray();
        }
    }

    [Fact]
    public void AgainstNullTest()
    {
        var ex = Assert.Throws<ArgumentNullException>(() => Validate.AgainstNull(string.Empty));

        Assert.True(ex.Message.EndsWith("str"));
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接