Предположения
Предположим, у меня есть класс со свойством:
class ClassWithProperty
{
public string Prop { get; private set; }
public ClassWithProperty(string prop)
{
this.Prop = prop;
}
}
А теперь предположим, что я создал экземпляр этого класса:
var test = new ClassWithProperty("test value");
Чего я хочу
Я хочу, чтобы это было распечатано на консоли:
Prop = 'test value'
Кусок пирога! Я использую этот код для получения желаемого результата:
Console.WriteLine("Prop = '{1}'", test.Prop);
Проблема
Я нарушаю DRY, потому что "Prop" дважды встречается в приведенном выше коде. Если я реорганизую класс и изменю имя свойства, мне также придется изменить строковый литерал. Также есть много шаблонного кода, если бы у меня был класс со многими свойствами.
Предлагаемое решение
string result = buildString(() => c.Prop);
Console.WriteLine(result);
Где метод buildString выглядит так:
private static string buildString(Expression<Func<string>> expression)
{
MemberExpression memberExpression = (MemberExpression)expression.Body;
string propertyName = memberExpression.Member.Name;
Func<string> compiledFunction = expression.Compile();
string propertyValue = compiledFunction.Invoke();
return string.Format("{0} = '{1}'", propertyName, propertyValue);
}
Вопрос
Вышеупомянутое решение работает нормально, и я доволен им, но если есть более простой и менее «страшный» способ решить эту проблему, это сделает меня намного счастливее. Есть ли более легкая альтернатива для достижения того же результата с меньшим и более простым кодом? Может, что-нибудь без деревьев выражений?
Изменить
Основываясь на прекрасной идее Ману (см. Ниже), я мог бы использовать этот метод расширения:
static public IEnumerable<string> ListProperties<T>(this T instance)
{
return instance.GetType().GetProperties()
.Select(p => string.Format("{0} = '{1}'",
p.Name, p.GetValue(instance, null)));
}
Он отлично подходит для получения строкового представления всех свойств экземпляра.
Но: как из этого перечисления выбрать типизированное конкретное свойство? Я бы снова использовал выражение «деревья» ... или я не вижу леса за деревьями?
Изменить 2
Использование здесь отражений или деревьев выражений - дело вкуса.
Идея Люка использовать инициализаторы проекции просто гениальна. Из его ответа я наконец создаю этот метод расширения (который в основном представляет собой версию его ответа LINQ):
public static IEnumerable<string> BuildString(this object source)
{
return from p in source.GetType().GetProperties()
select string.Format("{0} = '{1}'", p.Name, p.GetValue(source, null));
}
Теперь звонок будет выглядеть так:
new { c.Prop }.BuildString().First()
Это выглядит немного лучше, чем мой исходный вызов с лямбдой (но, думаю, это тоже дело вкуса). Предложение Люка, однако, превосходит мое решение, поскольку оно позволяет указать сколько угодно свойств (см. Ниже).