获取属性名称为字符串

(见下面我用我接受的答案创建的解决方案)

我试图提高一些涉及反射的代码的可维护性。该应用程序有一个。net Remoting接口公开了一个名为Execute的方法,用于访问未包含在其发布的远程接口中的应用程序部分。

下面是应用程序如何指定属性(本例中的静态属性),这意味着可以通过Execute访问:

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

因此远程用户可以调用:

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

应用程序将使用反射来查找SomeClass。SomeProperty并以字符串形式返回它的值。

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

我需要的是:

SomeClass.SomeProperty.GetTheNameOfThisPropertyAsAString()

作为ExposeProperty中的第三个parm,这样重构工具就会照顾到重命名。

有办法做到这一点吗?

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

// <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方法了。擦门把手是危险的工作。

300716 次浏览

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

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

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

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

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

看到http://msdn.microsoft.com/en-us/library/system.diagnostics.stacktrace (VS.71) . aspx

有一个著名的黑客从lambda表达式中提取它(这是来自PropertyObserver类,由Josh Smith在他的MVVM基金会):

    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基金会的完整代码。

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

  1. < p > Type.GetProperties()方法

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

Is this what you need?

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

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

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"

基于已经在问题和本文中的答案: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));
}
}

老问题了,但是这个问题的另一个答案是在使用CallerMemberNameAttribute的helper类中创建一个静态函数。

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

然后像这样使用它:

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

在c# 6.0中,这不是问题,因为你可以这样做:

nameof(SomeProperty)

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

名称的MSDN文档

我一直在用这个答案来达到很好的效果:从Expression<Func<TModel,TProperty>>

我意识到我之前已经回答过这个问题。我的其他答案的唯一优点是它适用于静态属性。我发现这个答案中的语法更有用,因为您不必创建想要反映的类型的变量。

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

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

我在使用已经为我的特定用例建议的解决方案时遇到了一些困难,但最终解决了这个问题。我不认为我的具体情况值得一个新的问题,所以我在这里张贴我的解决方案供参考。(这与问题密切相关,并为其他与我有类似情况的人提供了解决方案)。

我最终得到的代码是这样的:

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,这使得其他开发人员更容易得到它。

我的解决方案与公认答案之间的主要区别是,我的类是泛型的,我只想匹配泛型类型中的布尔型属性。

这就是我实现它的方式,背后的原因是如果你想从它的成员中获取名称的类不是静态的,那么你需要创建它的一个实例,然后获取成员的名称。所以这里的一般性就有用了

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'