2

I could have a method like this:

public void MyMethod<T, TResult>( string propertyName, ... ){
    var name = propertyName;
    return {property with correct name from some object that is of type TResult}
}

And call it like this:

MyMethod<SomeClass>("SomePropertyName");

To get hold of the propertyname inside the method. However, I do not like sending that propertyname in as a string in case SomeClass changes in the future, and the compiler cannot verify that the propertyName matches a property of type TResult either.

I would much rather call it like this:

MyMethod<SomeClass>(c => c.SomePropertyName);

But I am unsure how my method would look like then. I have tried variants of this:

public void MyMethod<T>( Func<T,TResult> property, ... ){
    var name = {do some cleverness on property here to extract the actual name of the property inside the expression};
    return {property with correct name from some object that is of type TResult}
}

Are there any good clean way to do this in C#?

Øyvind Bråthen
  • 59,338
  • 27
  • 124
  • 151
  • What are you actually trying to accomplish here? I think you're func version is probably the right way to go. – C Bauer Sep 09 '14 at 12:30
  • How about this? http://stackoverflow.com/questions/13074202/passing-strongly-typed-property-name-as-argument – Patrick Hofman Sep 09 '14 at 12:30
  • @CBauer - I thought that as well, but how can I get the actual name of the property as a string in my second example? So I would need to retrieve "SomePropertyName" from the Func property in some way, but I haven't figured out how. – Øyvind Bråthen Sep 09 '14 at 12:31
  • It should not be a `Func`, it should be an `Expression>` and then `((MemberExpression)expression.Body).Member.Name` – takemyoxygen Sep 09 '14 at 12:33
  • [Is this what you're after](http://stackoverflow.com/questions/671968/retrieving-property-name-from-lambda-expression) ? – Sriram Sakthivel Sep 09 '14 at 12:34
  • Are you trying to map one type to another? There's a lot of libraries out there for that which will do what you're asking, like Automapper. – C Bauer Sep 09 '14 at 12:37

3 Answers3

3

You can't investigate Func<> delegates with much detail. You want to interrogate an Expression.. something like this (not tested.. but should be close):

public void MyMethod<T>(Expression<Func<T,TResult>> expr, ... ){
    var expression = (MemberExpression)expr.Body;
    var name = expression.Member.Name;

    // .. the rest here using "name"
}

Usage is the same:

MyMethod<User>(u => u.UserId); // name will be "UserId"
Øyvind Bråthen
  • 59,338
  • 27
  • 124
  • 151
Simon Whitehead
  • 63,300
  • 9
  • 114
  • 138
  • Perfect. It seems like Expression was the missing part of the puzzle here. Thanks a lot :) – Øyvind Bråthen Sep 09 '14 at 12:38
  • No problem. Thanks for the typo fix up. I normally use `x` for every lambda.. but changed to `u` to show it as a user half way through typing. I think I should head to bed! :) – Simon Whitehead Sep 09 '14 at 12:39
  • Good thing for me you stayed up to answer this before tucking in ;) – Øyvind Bråthen Sep 09 '14 at 12:40
  • why use `u` in the lambda ? you can simplify with `()` – Xaruth Sep 09 '14 at 12:41
  • @Xaruth - How will that be simplified? You'll need a object to call the property on. – Øyvind Bråthen Sep 09 '14 at 12:44
  • because you don't need parameter, just the expression. So `() => u.UserId` is enough – Xaruth Sep 09 '14 at 12:47
  • @Xaruth - First of, in your example above `u` will be undefined. In addition, even if it worked in that way, how some `() => u.UserId` is simpler than `u => u.UserId`? The last one reads better for me at least. – Øyvind Bråthen Sep 10 '14 at 05:44
  • @ØyvindBråthen Sorry, in my mind, `u` was a defined object. When you write your code, intelisens will work for you if you write `() => this.MyProperty` or `() => MyObject.MyProperty` but `u` will never be understood (neither intelisens and build : `Delegate 'System.Func' does not take 1 arguments`). Or I miss something ... – Xaruth Sep 10 '14 at 08:30
  • @Xaruth - Yes, `() => this.MyProperty` I thoink should work, but I find `u => u.UserId` much more readable. Matter of preference I guess ;) – Øyvind Bråthen Sep 10 '14 at 08:31
  • btw, you validate this answer (`something like this (not tested.. but should be close)`), but it doesn't even build. Just don't undertand why you prefer this answer than mine ;) – Xaruth Sep 10 '14 at 08:32
  • @Xaruth you will find that quite a few of my answers don't build, as I merely build upon the pseudo-code that the person asking the question has put in their question, steering them towards a solution. It also helps the person asking the question to recognise the newer constructs when you include some of their own code/pseudo-code. That is just my personal preference in the way I answer questions on StackOverflow. Your answer is great, so don't worry about that :) – Simon Whitehead Sep 10 '14 at 10:14
1

This is how I do this for a RaisePropertyChanged method

internal static class PropertySupport
{
    public static string ExtractPropertyName<T>(Expression<Func<T>> propertyExpression)
    {
        if (propertyExpression == null)
        {
            throw new ArgumentNullException("propertyExpression");
        }

        var memberExpression = propertyExpression.Body as MemberExpression;
        if (memberExpression == null)
        {
            throw new ArgumentException("propertyExpression");
        }

        var property = memberExpression.Member as PropertyInfo;
        if (property == null)
        {
            throw new ArgumentException("propertyExpression");
        }

        var getMethod = property.GetGetMethod(true);
        if (getMethod.IsStatic)
        {
            throw new ArgumentException("propertyExpression");
        }

        return memberExpression.Member.Name;
    }
}

and in the method using the PropertySupport :

protected virtual void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpression)
{
    var propertyName = PropertySupport.ExtractPropertyName(propertyExpression);
    this.RaisePropertyChanged(propertyName);
}

I can use it simply with

RaisePropertyChanded<String>(() => this.MyString);

as you can see, the lambda is very simple.

Xaruth
  • 4,034
  • 3
  • 19
  • 26
0

Would [callermembername] work for you? You'd use it like:

public void MyMethod<T>([CallerMemberName]string caller = null){
    //leave caller blank and it will put the name of the calling member in as a string
}

http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.callermembernameattribute(v=vs.110).aspx

C Bauer
  • 5,003
  • 4
  • 33
  • 62
  • `[CallerMemberName]` have been introduced in Framework 4.5 (but can be included in other framework by coding them) – Xaruth Sep 09 '14 at 12:35