In C# 6 and higher, the syntactical goodness of using static
can make usages of this sort of method much more readable. It will also give you early binding / compile-time checks, so CodeLens will show you usages to your property that used to be referenced as string literals (like in lots of data binding code floating around out there). This makes refactoring much easier when you have to rename properties down the road.
The code here builds on @abatishchev's answer, because in this scenario (where you don't have an instance of the type in question), using extension methods makes the code more verbose, not less.
There’s also an additional line in the PropertyName
method to handle the point by @pedro-faustino about exceptions casting UnaryExpression. (That wouldn’t be needed if the method had a second type parameter in the propertyExpression
argument, for the return type, e.g. Expression<Func<T, TMember>>
).
That said, here’s an example of how you could wire up data bindings in an early-bound manner with this kind of strategy:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using BusinessObjects.Vehicles;
using static MyCoolHelperMethods;
namespace Helpers
{
public static class MyCoolHelperMethods
{
public static string PropertyName<T>(Expression<Func<T, object>> propertyExpression) where T : class
{
var memberExpression = propertyExpression.Body as MemberExpression
?? (propertyExpression.Body as UnaryExpression)?.Operand as MemberExpression;
return memberExpression?.Member.Name;
}
}
}
namespace Application
{
using System.Windows.Forms;
public class Car
{
public string Make {get; set;}
public string Model {get; set;}
}
// imagine this is a form with textboxes for car Make and Model:
public partial class MyWindowsForm
{
public MyWindowsForm()
{
var car = new Car();
this.txtCarMake.DataBindings.Add(new Binding(
propertyName: PropertyName<TextBox>(x => x.Text),
dataSource: car,
dataMember: PropertyName<Car>(x => x.Make),
// formattingEnabled is needed to avoid invalid cast
// exceptions assigning the object property to the control:
formattingEnabled: true));
this.txtCarModel.DataBindings.Add(new Binding(
propertyName: PropertyName<TextBox>(x => x.Text),
dataSource: car,
dataMember: PropertyName<Car>(x => x.Model),
// formattingEnabled is needed to avoid invalid cast
// exceptions assigning the object property to the control:
formattingEnabled: true));
}
}
}