Получить имя свойства как строку
(См. ниже решение, которое я создал, используя ответ, который я принял)
Я пытаюсь улучшить ремонтопригодность некоторого кода, включающего отражение. Приложение имеет интерфейс .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. Полировка дверных ручек - опасная работа...
Спасибо всем.
Ответы
Ответ 1
Используя GetMemberInfo здесь: Извлечение имени свойства из выражения лямбда, вы можете сделать что-то вроде этого:
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);
}
}
Ответ 2
С С# 6.0 это теперь не проблема, как вы можете:
nameof(SomeProperty)
Это выражение разрешено во время компиляции до "SomeProperty"
.
Документация MSDN имениOf.
Ответ 3
Там хорошо известный хак, чтобы извлечь его из выражения лямбда (это из класса 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.
Ответ 4
Хорошо, вот что я создал (основанный на ответе, который я выбрал, и на вопросе, на который он ссылался):
// <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);
Ответ 5
Вы можете использовать Reflection для получения фактических названий свойств.
http://www.csharp-examples.net/reflection-property-names/
Если вам нужен способ присвоить свойство "String Name", почему бы вам не написать атрибут, который вы можете отобразить, чтобы получить имя строки?
[StringName("MyStringName")]
private string MyProperty
{
get { ... }
}
Ответ 6
Класс PropertyInfo должен помочь вам достичь этого, если я правильно понял.
-
Метод Type.GetProperties()
PropertyInfo[] propInfos = typeof(ReflectedType).GetProperties();
propInfos.ToList().ForEach(p =>
Console.WriteLine(string.Format("Property name: {0}", p.Name));
Это то, что вам нужно?
Ответ 7
Я изменил ваше решение на цепочку с несколькими свойствами:
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"
Ответ 8
Основываясь на ответе, который уже находится в вопросе и в этой статье: 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));
}
}
Ответ 9
Старый вопрос, но еще один ответ на этот вопрос - создать статическую функцию в вспомогательном классе, который использует CallerMemberNameAttribute.
public static string GetPropertyName([CallerMemberName] String propertyName = null) {
return propertyName;
}
И затем используйте его как:
public string MyProperty {
get { Console.WriteLine("{0} was called", GetPropertyName()); return _myProperty; }
}
Ответ 10
Вы можете использовать класс StackTrace, чтобы получить имя текущей функции (или если вы поместили код в функцию, а затем понизите уровень и получите вызывающую функцию).
См. http://msdn.microsoft.com/en-us/library/system.diagnostics.stacktrace(VS.71).aspx
Ответ 11
Я использовал этот ответ с большим эффектом: Получить свойство в виде строки из выражения Expression < Func < TModel, TProperty → >
Я понимаю, что я уже немного ответил на этот вопрос. Единственное преимущество, которое имеет мой другой ответ, заключается в том, что он работает для статических свойств. Я считаю, что синтаксис в этом ответе намного полезнее, потому что вам не нужно создавать переменную типа, который вы хотите отразить.
Ответ 12
У меня возникли трудности с использованием решений, которые уже были предложены для моего конкретного случая использования, но в конце концов выяснили это. Я не думаю, что мой конкретный случай заслуживает нового вопроса, поэтому я размещаю свое решение здесь для справки. (Это очень тесно связано с вопросом и дает решение для всех, кто имеет аналогичный случай для моего).
Код, который я закончил, выглядит следующим образом:
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
, что облегчает для других разработчиков право на это.
Основное различие между моим решением и принятым ответом состоит в том, что мой класс является общим, и я хочу только сопоставить свойства из общего типа, которые являются логическими.
Ответ 13
как я его реализовал, причина в том, что класс, который вы хотите получить от него, не является статическим, вам нужно создать его, а затем получить имя участника. поэтому родовой здесь помогает
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'