以字符串形式获取属性名称


203

(请参阅下面我使用接受的答案创建的解决方案)

我正在尝试改善某些涉及反射的代码的可维护性。该应用程序具有.NET Remoting接口,该接口(除其他外)公开了一种称为Execute的方法,用于访问应用程序未包含在其发布的远程接口中的部分。

这是应用程序指定通过Execute可以访问的属性(在此示例中为静态属性)的方式:

RemoteMgr.ExposeProperty("SomeSecret", typeof(SomeClass), "SomeProperty");

因此,远程用户可以致电:

string response = remoteObject.Execute("SomeSecret");

然后该应用将使用反射来查找SomeClass.SomeProperty并将其值作为字符串返回。

不幸的是,如果有人重命名SomeProperty而忘记更改ExposeProperty()的第3个参数,则会破坏此机制。

我需要相当于:

SomeClass.SomeProperty.GetTheNameOfThisPropertyAsAString()

用作ExposeProperty中的第三个参数,因此重构工具将负责重命名。

有没有办法做到这一点?提前致谢。

好的,这就是我最终创建的内容(基于我选择的答案和他引用的问题):

// <summary>
// Get the name of a static or instance property from a property access lambda.
// </summary>
// <typeparam name="T">Type of the property</typeparam>
// <param name="propertyLambda">lambda expression of the form: '() => Class.Property' or '() => object.Property'</param>
// <returns>The name of the property</returns>
public string GetPropertyName<T>(Expression<Func<T>> propertyLambda)
{
    var me = propertyLambda.Body as MemberExpression;

    if (me == null)
    {
        throw new ArgumentException("You must pass a lambda of the form: '() => Class.Property' or '() => object.Property'");
    }

    return me.Member.Name;
 }

用法:

// Static Property
string name = GetPropertyName(() => SomeClass.SomeProperty);

// Instance Property
string name = GetPropertyName(() => someObject.SomeProperty);

现在有了这种出色的功能,是时候简化ExposeProperty方法了。抛光门把手是危险的工作。

感谢大家。


9
真正的理由是您添加了解决方案并将其捆绑在一起。
Simply G.


您应该将解决方案添加为答案–它比您接受的答案更为简洁。
肯尼·埃维特

1
@肯尼·埃维特(Kenny Evitt):完成:)
吉姆·C

@JimC赞!并链接到当前接受的答案的评论中。谢谢!
肯尼·埃维特

Answers:


61

从此处使用GetMemberInfo:从lambda表达式检索属性名称,您可以执行以下操作:

RemoteMgr.ExposeProperty(() => SomeClass.SomeProperty)

public class SomeClass
{
    public static string SomeProperty
    {
        get { return "Foo"; }
    }
}

public class RemoteMgr
{
    public static void ExposeProperty<T>(Expression<Func<T>> property)
    {
        var expression = GetMemberInfo(property);
        string path = string.Concat(expression.Member.DeclaringType.FullName,
            ".", expression.Member.Name);
        // Do ExposeProperty work here...
    }
}

public class Program
{
    public static void Main()
    {
        RemoteMgr.ExposeProperty("SomeSecret", () => SomeClass.SomeProperty);
    }
}

太酷了 看起来它也适用于任何属性类型。
Jim C 2010年

我只是尝试了实例和静态属性。到目前为止,一切都很好。
Jim C

知道在哪里可以获取包含的程序集或NuGet软件包GetMemberInfo吗?我无法使用Microsoft Enterprise Library的“通用实用程序”包找到任何内容,这是MSDN似乎表明包含该方法的内容。有一个“非官方”的程序包,但非官方的则无济于事。JimC的答案(基于此答案)更加简洁,并且不依赖于看似不可用的库。
肯尼·埃维特

1
@KennyEvitt,他引用的方法是他所链接问题的作者所写的一种。替代到methodyou可以使用此Type.GetMembers msdn.microsoft.com/en-us/library/...
苯教

464

对于C#6.0,现在可以这样做了,这不是问题:

nameof(SomeProperty)

该表达式在编译时解析为"SomeProperty"

nameof的MSDN文档


18
这很糟糕,对于ModelState.AddModelError调用非常有用。
迈克尔·西尔弗

9
这是一个const string!很棒
杰克

4
@RaidenCore可以肯定的是,如果您正在为微控制器编写程序,则应使用像C这样的低级语言,并且如果您需要压缩图像和视频处理的所有性能,则应该使用C或C ++。但对于其他95%的应用程序,托管代码框架将足够快。最终,C#也被编译为机器代码,如果需要,您甚至可以将其预编译为本地代码。
Tsahi Asher

2
顺便说一下,@ RaidenCore,您提到的应用早于C#,这就是为什么它们是用C ++编写的原因。如果他们今天写的,谁知道使用什么语言。参见例如Paint.NET。
Tsahi Asher

1
当您想RaiseProperty在WPF中使用时,这真的很有用!使用RaisePropertyChanged(nameof(property))代替RaisePropertyChanged(“ property”)
Pierre

17

有一个著名的技巧可以从lambda表达式中提取出来(这是Josh Smith在其MVVM基础上的PropertyObserver类):

    private static string GetPropertyName<TPropertySource>
        (Expression<Func<TPropertySource, object>> expression)
    {
        var lambda = expression as LambdaExpression;
        MemberExpression memberExpression;
        if (lambda.Body is UnaryExpression)
        {
            var unaryExpression = lambda.Body as UnaryExpression;
            memberExpression = unaryExpression.Operand as MemberExpression;
        }
        else
        {
            memberExpression = lambda.Body as MemberExpression;
        }

        Debug.Assert(memberExpression != null, 
           "Please provide a lambda expression like 'n => n.PropertyName'");

        if (memberExpression != null)
        {
            var propertyInfo = memberExpression.Member as PropertyInfo;

            return propertyInfo.Name;
        }

        return null;
    }

抱歉,缺少某些上下文。这是一个更大的类的一部分,其中TPropertySource包含该属性的类。您可以在TPropertySource中使该函数通用,以从类中提取它。我建议您看一下MVVM Foundation的完整代码。


举一个如何调用该函数的示例,它当然是+1。糟糕,调试断言中没有内容-这就是为什么使开发人员水平滚动以到达一行的重要部分是邪恶的;)
OregonGhost 2010年

嗯...我需要剖析这一部分才能理解它。
Jim C 2010年

Visual Studio 2008将“ TPropertySource”标记为错误(“找不到”)。
Jim C

我只是意识到它的类型名称,而不仅仅是C ++中的符号<T>。TPropertySource代表什么?
Jim C

2
要进行此编译,您只需将方法签名更改为read,public static string GetPropertyName<TPropertySource>(Expression<Func<TPropertySource, object>> expression)然后像这样调用:var name = GetPropertyName<TestClass>(x => x.Foo);
dav_i 2014年

16

好的,这就是我最终创建的内容(基于我选择的答案和他引用的问题):

// <summary>
// Get the name of a static or instance property from a property access lambda.
// </summary>
// <typeparam name="T">Type of the property</typeparam>
// <param name="propertyLambda">lambda expression of the form: '() => Class.Property' or '() => object.Property'</param>
// <returns>The name of the property</returns>

public string GetPropertyName<T>(Expression<Func<T>> propertyLambda)
{
    var me = propertyLambda.Body as MemberExpression;

    if (me == null)
    {
        throw new ArgumentException("You must pass a lambda of the form: '() => Class.Property' or '() => object.Property'");
    }

    return me.Member.Name;
 }

用法:

// Static Property
string name = GetPropertyName(() => SomeClass.SomeProperty);

// Instance Property
string name = GetPropertyName(() => someObject.SomeProperty);

8

的PropertyInfo类应该可以帮助您实现这一点,如果我理解正确。

  1. Type.GetProperties()方法

    PropertyInfo[] propInfos = typeof(ReflectedType).GetProperties();
    propInfos.ToList().ForEach(p => 
        Console.WriteLine(string.Format("Property name: {0}", p.Name));

这是您需要的吗?


不,虽然我在应用程序收到“ SomeSecret”请求时确实使用GetProperties。该应用程序在地图中查找“ SomeSecret”,发现需要在名为“ SomeClass”的类中找到名为“ SomeProperty”的属性。
Jim C

实际上,nameof(SomeProperty)从.net 4.0开始简化了此过程。不需要这么长时间的黑客攻击。
Div Tiwari

6

您可以使用Reflection获取属性的实际名称。

http://www.csharp-examples.net/reflection-property-names/

如果需要一种为属性分配“字符串名称”的方法,为什么不编写一个可以反映以获取字符串名称的属性呢?

[StringName("MyStringName")]
private string MyProperty
{
    get { ... }
}

1
是的,这就是应用程序处理“ SomeSecret”的传入请求的方式,但是它没有给我提供有关ExposeProperty问题的工具。
Jim C

有趣的是...那么您可以将MyProperty重命名为您喜欢的内容,只要您不与MyStringName混淆,并且如果出于某种原因想要更改它,则需要修改ExposeProperty参数。至少我可以在属性旁边添加一条带有警告的注释,因为您必须查看它才能更改属性的值(与重命名属性不同,可以在任何引用位置进行更改)。
Jim C 2010年

6

我修改了您的解决方案以链接多个属性:

public static string GetPropertyName<T>(Expression<Func<T>> propertyLambda)
{
    MemberExpression me = propertyLambda.Body as MemberExpression;
    if (me == null)
    {
        throw new ArgumentException("You must pass a lambda of the form: '() => Class.Property' or '() => object.Property'");
    }

    string result = string.Empty;
    do
    {
        result = me.Member.Name + "." + result;
        me = me.Expression as MemberExpression;
    } while (me != null);

    result = result.Remove(result.Length - 1); // remove the trailing "."
    return result;
}

用法:

string name = GetPropertyName(() => someObject.SomeProperty.SomeOtherProperty);
// returns "SomeProperty.SomeOtherProperty"

4

基于问题中已有的答案以及本文的内容:https : //handcraftsman.wordpress.com/2008/11/11/how-to-get-c-property-names-without-magic-strings/我介绍我对这个问题的解决方案:

public static class PropertyNameHelper
{
    /// <summary>
    /// A static method to get the Propertyname String of a Property
    /// It eliminates the need for "Magic Strings" and assures type safety when renaming properties.
    /// See: http://stackoverflow.com/questions/2820660/get-name-of-property-as-a-string
    /// </summary>
    /// <example>
    /// // Static Property
    /// string name = PropertyNameHelper.GetPropertyName(() => SomeClass.SomeProperty);
    /// // Instance Property
    /// string name = PropertyNameHelper.GetPropertyName(() => someObject.SomeProperty);
    /// </example>
    /// <typeparam name="T"></typeparam>
    /// <param name="propertyLambda"></param>
    /// <returns></returns>
    public static string GetPropertyName<T>(Expression<Func<T>> propertyLambda)
    {
        var me = propertyLambda.Body as MemberExpression;

        if (me == null)
        {
            throw new ArgumentException("You must pass a lambda of the form: '() => Class.Property' or '() => object.Property'");
        }

        return me.Member.Name;
    }
    /// <summary>
    /// Another way to get Instance Property names as strings.
    /// With this method you don't need to create a instance first.
    /// See the example.
    /// See: https://handcraftsman.wordpress.com/2008/11/11/how-to-get-c-property-names-without-magic-strings/
    /// </summary>
    /// <example>
    /// string name = PropertyNameHelper((Firma f) => f.Firmenumsatz_Waehrung);
    /// </example>
    /// <typeparam name="T"></typeparam>
    /// <typeparam name="TReturn"></typeparam>
    /// <param name="expression"></param>
    /// <returns></returns>
    public static string GetPropertyName<T, TReturn>(Expression<Func<T, TReturn>> expression)
    {
        MemberExpression body = (MemberExpression)expression.Body;
        return body.Member.Name;
    }
}

还有一个Test,它还显示实例和静态属性的用法:

[TestClass]
public class PropertyNameHelperTest
{
    private class TestClass
    {
        public static string StaticString { get; set; }
        public string InstanceString { get; set; }
    }

    [TestMethod]
    public void TestGetPropertyName()
    {
        Assert.AreEqual("StaticString", PropertyNameHelper.GetPropertyName(() => TestClass.StaticString));

        Assert.AreEqual("InstanceString", PropertyNameHelper.GetPropertyName((TestClass t) => t.InstanceString));
    }
}

3

旧的问题,但另一个解决方案是在使用CallerMemberNameAttribute的帮助器类中创建一个静态函数。

public static string GetPropertyName([CallerMemberName] String propertyName = null) {
  return propertyName;
}

然后像这样使用它:

public string MyProperty {
  get { Console.WriteLine("{0} was called", GetPropertyName()); return _myProperty; }
}

0

您可以使用StackTrace类获取当前函数的名称(或者,如果将代码放入函数中,则下一级并获取调用函数)。

请参阅http://msdn.microsoft.com/zh-cn/library/system.diagnostics.stacktrace(VS.71).aspx


我不知道您想在哪里捕获堆栈跟踪,但是我想不出其中包含属性名称的对象。
Jim C

您可以这样做,但是由于编译器内联优化,这可能导致意外结果(包括异常)。smelser.net/blog/post/2008/11/27/...
JoeGeeky


0

在使用针对我的特定用例建议的解决方案时,我遇到了一些困难,但最终找到了解决方案。我认为我的具体案例不值得提出新的问题,因此我在此处发布我的解决方案以供参考。(这与问题密切相关,并为其他任何与我类似的案例提供了解决方案)。

我最终得到的代码如下所示:

public class HideableControl<T>: Control where T: class
{
    private string _propertyName;
    private PropertyInfo _propertyInfo;

    public string PropertyName
    {
        get { return _propertyName; }
        set
        {
            _propertyName = value;
            _propertyInfo = typeof(T).GetProperty(value);
        }
    }

    protected override bool GetIsVisible(IRenderContext context)
    {
        if (_propertyInfo == null)
            return false;

        var model = context.Get<T>();

        if (model == null)
            return false;

        return (bool)_propertyInfo.GetValue(model, null);
    }

    protected void SetIsVisibleProperty(Expression<Func<T, bool>> propertyLambda)
    {
        var expression = propertyLambda.Body as MemberExpression;
        if (expression == null)
            throw new ArgumentException("You must pass a lambda of the form: 'vm => vm.Property'");

        PropertyName = expression.Member.Name;
    }
}

public interface ICompanyViewModel
{
    string CompanyName { get; }
    bool IsVisible { get; }
}

public class CompanyControl: HideableControl<ICompanyViewModel>
{
    public CompanyControl()
    {
        SetIsVisibleProperty(vm => vm.IsVisible);
    }
}

对我来说重要的是,在CompanyControl该类中,编译器仅允许我选择一个布尔属性,ICompanyViewModel该属性使其他开发人员更容易正确地进行设置。

我的解决方案和接受的答案之间的主要区别在于,我的类是通用的,而我只想匹配布尔类型的通用属性。


0

这就是我的实现方式,其背后的原因是,如果要从其成员获取名称的类不是静态的,则需要创建一个实例,然后获取该成员的名称。所以通用这里来帮助

public static string GetName<TClass>(Expression<Func<TClass, object>> exp)
{
    MemberExpression body = exp.Body as MemberExpression;

    if (body == null)
    {
         UnaryExpression ubody = (UnaryExpression)exp.Body;
         body = ubody.Operand as MemberExpression;
    }

     return body.Member.Name;
}

用法是这样的

var label = ClassExtension.GetName<SomeClass>(x => x.Label); //x is refering to 'SomeClass'
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.