2010-05-12 2 views
145

(см ниже решения я создал с помощью ответа я принял)Получить имя имущества в виде строки

Я пытаюсь улучшить ремонтопригодность некоторого кода с участием отражения. Приложение имеет интерфейс .NET Remoting, который предоставляет (среди прочего) метод, называемый Execute для доступа к частям приложения, не включенному в его опубликованный удаленный интерфейс.

Вот как приложение определяет свойства (статический один в этом примере), которые предназначены, чтобы быть доступными через Execute:

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

Так удаленный пользователь может позвонить:

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

и приложение будет использовать отражение, чтобы найти SomeClass.SomeProperty и вернуть его значение в виде строки.

К сожалению, если кто-то переименовывает SomeProperty и забывает изменить 3-е место в ExposeProperty(), он нарушает этот механизм.

Мне нужно эквивалент:

SomeClass.SomeProperty.GetTheNameOfThisPropertyAsAString() 

использовать в качестве 3-го Парма в 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. Полировка дверных ручек - опасная работа ...

Спасибо всем.

+6

Ее действительно appriciated, что вы добавили раствор и привязанные вещи. –

+0

Возможный дубликат [Получить имя и тип свойства с использованием выражения лямбда] (http://stackoverflow.com/questions/273941/get-property-name-and-type-using-lambda-expression) – nawfal

+0

Вы должны добавить свое решение как ответ - это гораздо более краткий, чем ответ, который вы приняли. –

ответ

55

Редактировать: Этот ответ был написан до C# 6 и устарел. Please see James' answer below.


Использование GetMemberInfo здесь: Retrieving Property name from lambda expression вы можете сделать что-то вроде этого:

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

Это круто. Похоже, он будет работать и над любым типом собственности. –

+0

Я просто попробовал его как с экземпляром, так и со статическими свойствами. Все идет нормально. –

+0

Любая идея, где я могу получить сборку или пакет NuGet, содержащий «GetMemberInfo»? Я не могу найти ничего с пакетом «common utilities» для Microsoft Enterprise Library, который, по-видимому, указывает MSDN, содержит этот метод. Есть «неофициальный» пакет, но неофициальный - скучный. [Ответ JimC] (http://stackoverflow.com/a/35095504/173497), который основан на этом, намного более краток и не полагается на, казалось бы, недоступную библиотеку. –

6

Вы можете использовать Reflection для получения фактических названий свойств.

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

Если вам нужен способ, чтобы назначить «Name String» в собственность, то почему бы вам не написать атрибут, который вы можете поразмышлять над получить имя строки?

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

Ya, вот как приложение обрабатывает входящие запросы для «SomeSecret», но это не дает мне инструмента для проблемы ExposeProperty. –

+0

@Jim: см. Мое редактирование –

+0

Интересно ... тогда вы могли бы переименовать MyProperty в контент вашего сердца, пока вы не возитесь с MyStringName, и если по какой-то причине вы хотите его изменить, вам нужно изменить ExposeProperty PARM. По крайней мере, я мог бы добавить комментарий рядом с атрибутом с таким предупреждением, так как вы должны смотреть на него, чтобы изменить значение атрибута (в отличие от переименования свойства, которое можно сделать из любого ссылочного местоположения). –

0

Вы можете использовать класс StackTrace, чтобы получить имя текущей функции, (или если поместить код в функцию, а затем шаг вниз уровень и получить функцию вызова).

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

+0

Я не знаю, где вы имели в виду захват трассировки стека, но я не могу думать о том, что будет содержать имя свойства. –

+0

Вы можете сделать это, но это может привести к неожиданным результатам (в том числе исключениям) из-за оптимизации подстроки компилятора. http://www.smelser.net/blog/post/2008/11/27/Be-carefull-with-that-Stack-Trace.aspx – JoeGeeky

15

Там очень хорошо известный хак, чтобы извлечь его из лямбда-выражения (это из класса PropertyObserver, Джош Смит в своем 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 - класс, содержащий свойство.Вы можете сделать функцию generic в TPropertySource, чтобы извлечь ее из класса. Я рекомендую взглянуть на полный код с MVVM Foundation.

+0

С примером того, как вызвать функцию, это, безусловно, +1 , К сожалению, не было видно, что в утверждении отладки есть один - вот почему разработчик горизонтально прокручивает, чтобы добраться до важной части строки, является злой;) – OregonGhost

+0

Хммм ... Мне нужно вскрыть это, чтобы понять это. –

+0

Флаги Visual Studio 2008 «TPropertySource» как ошибка («невозможно найти»). –

6

Класс PropertyInfo должен помочь вам достичь этого, если я правильно понимаю.

  1. Type.GetProperties() method

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

Является ли это то, что вам нужно?

+0

Нет, хотя я использую GetProperties, когда приложение получает запрос на« SomeSecret ». Приложение просматривает «SomeSecret» на карте, чтобы обнаружить, что ему нужно найти свойство «SomeProperty» в классе «SomeClass». –

5

Я изменил свое решение в цепи по нескольким свойствам:

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

и тест, который также показывает использование для экземпляра и статические свойства:

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

Старый вопрос, но другой ответ на этот вопрос заключается в создании статической функции в вспомогательном классе, который использует CallerMemberNameAttribute.

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

И затем использовать его как:

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

С C# 6.0, теперь это не проблема, как вы можете сделать:

nameof(SomeProperty) 

Это выражение решается на compile- время до "SomeProperty".

MSDN documentation of nameof.

+11

Это плохо и очень полезно для ModelState .AddModelError. –

+0

Он также работает на VB (из Visual Studio 2015) – Max

+6

И это 'const string'! Amazing – Jack

0

Я использовал этот ответ с большим эффектом: Get the property, as a string, from an Expression<Func<TModel,TProperty>>

Я понимаю, что я уже ответил на этот вопрос некоторое время назад. Единственное преимущество, которое имеет мой другой ответ, заключается в том, что он работает для статических свойств. Я считаю, что синтаксис в этом ответе гораздо полезнее, потому что вам не нужно создавать переменную типа, который вы хотите отразить.

14

Хорошо, вот что я в конечном итоге создание (на основе ответа, который я выбрал, и вопрос, который он ссылается):

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