I would like to be able to retrieve the name of a property of a type using a strongly typed syntax. I already got a function to get a property name of an instance:
public static string PropertyName<T, TReturn>(this T obj, Expression<Func<T, TReturn>> property) where T : class
{
MemberExpression body = (MemberExpression) property.Body;
if (body == null) throw new ArgumentException("The provided expression did not point to a property.");
return body.Member.Name;
}
Which can be called like this:
Car car = new Car();
car.PropertyName(x => x.Wheels) //returns "Wheels"
I'm trying to create another function that could support the following:
Type t = Typeof(Car);
t.PropertyName(x => x.Wheels) //should return "Wheels"
Or just (even better!):
Car.PropertyName(x => x.Wheels)
How would I go about this?
You can rewrite your method to use it without creating an instance:
var prop = ReflectionHelper.PropertyName<Car>(x => x.Wheels);
because your don't use obj
inside because you don't need it:
public static class ReflectionHelper
{
public static string PropertyName<T>(Expression<Func<T, object>> property) where T : class
{
MemberExpression body = (MemberExpression)property.Body;
return body.Member.Name;
}
}
Note that the return type doesn't have to be strongly-typed, it can be just object
.
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));
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With