Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get a property name of a given type strongly typed? [duplicate]

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?

like image 939
JensOlsen112 Avatar asked Dec 09 '22 04:12

JensOlsen112


2 Answers

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.

like image 113
abatishchev Avatar answered Dec 10 '22 19:12

abatishchev


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));
        }
    }
}
like image 44
Paul Smith Avatar answered Dec 10 '22 19:12

Paul Smith