1

I have got this class:

class foo
{
   int val;
   public int Val
   {
      set{ val = values; },
      set{ val = values; }
   }
}

I need to pass the property name to a DataBinding:

String propertyName = "Val";
ctrl.DataBindings.Add(propertyName, object, dataMember, true, DataSourceUpdateMode.Never);

I want to do something like this:

propertyName = typeof(foo).methods.Val.toString();
dlopezgonzalez
  • 4,217
  • 5
  • 31
  • 42

5 Answers5

5

If you can use C#6, you have the nameof operator, which does just that.

string propertyName = nameof(foo.Val);

If you use C# 5, you can leverage expression trees:

public static string GetPropertyName<TParent>(Expression<Func<TParent, object>> prop)
{
    var expr = prop.Body;

    if (expr.NodeType == ExpressionType.Convert)
        expr = ((UnaryExpression)expr).Operand;

    if (expr.NodeType == ExpressionType.MemberAccess)
        return ((MemberExpression)expr).Member.Name;

    throw new ArgumentException("Invalid lambda", "prop");
}

Use this helper function like this (assuming it's in a ReflectionHelper class):

string propertyName = ReflectionHelper.GetPropertyName<foo>(x => x.Val);

This way, you can safely use refactorings in your IDE.

Lucas Trzesniewski
  • 50,214
  • 11
  • 107
  • 158
  • Thank you, this solution works on 4.5 .NET. I suppose that with this code the string ofuscation does not break the program. – dlopezgonzalez Dec 12 '14 at 10:55
  • @dlopezgonzalez Yes, the expression tree method shouldn't break in this case, as it references the assembly metadata. But the `nameof` method would still break if you change the property name after the build through obfuscation. – Lucas Trzesniewski Dec 12 '14 at 11:00
  • I hope, for example, the old eazfuscator broke the code when reflection was used. To avoid this, I needed to add this add the top of the class "[System.Reflection.ObfuscationAttribute(Feature = "properties renaming", Exclude = true)]". I hope this helps to another one leaving this here. – dlopezgonzalez Dec 12 '14 at 11:28
1

As of C# 6, you can use the nameof operator:

ctrl.DataBindings.Add(nameof(foo.Val), /* other arguments as before */);

Before C# 6, there's no really simple way to do this at compile-time. One option, however, is to have unit tests which check that all your property names are actual properties (checking with reflection).

Also note that in C# 5 there's CallerMemberNameAttribute which is useful for implementing INotifyPropertyChanged - but isn't as useful for your case.

The approach of using expression trees works, but it feels somewhat clunky to me. Although far lower tech, simple string constants and a unit test feels a bit simpler.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
1

If you are not using C# 6, you need to pass around an Expression<Func<T>>.

You can then do this with that object (if you are passing a property):

 private string GetPropertyName(Expression<Func<T>> propertyExpession)
 {
   //the cast will always succeed if properly used
   MemberExpression memberExpression = (MemberExpression)propertyExpression.Body;
   string propertyName = memberExpression.Member.Name;
   return propertyName;
 }

You would use this like:

var propName = GetPropertyName(() => this.Val);
toadflakz
  • 7,764
  • 1
  • 27
  • 40
  • Note that you don't want to be doing that too often, however - I don't *think* expression trees are cached. – Jon Skeet Dec 12 '14 at 10:45
  • I checked and you're right they're not. This is almost word for word the same code as used by PRISM's `OnPropertyChanged()` implementation tho' - so it can't be that bad, right? ;) – toadflakz Dec 12 '14 at 10:47
  • @JonSkeet most MVVM frameworks use that trick for INotifyPropertyChanged implementations, although the final code has to be a lot more robust. – Panagiotis Kanavos Dec 12 '14 at 10:47
  • 1
    @PanagiotisKanavos: That doesn't mean it's necessarily a good idea - and in fact in C# 5 you'd want to use `CallerMemberNameAttribute` for `INotifyPropertyChanged` anyway. – Jon Skeet Dec 12 '14 at 10:50
  • A framework can add its own caching. The leading frameworks have no performance problems so either they do cache, or the problem isn't so significant *in this scenario*. CallerMemberNameAttribute is restricted to passing the caller's name only so you can't update related properties. Only `nameof` is a full alternative – Panagiotis Kanavos Dec 12 '14 at 10:54
  • @JonSkeet: That's very interesting because PRISM 5.0 is in .NET 4.5 and doesn't use `CallerMemberName` for `INotifyPropertyChanged` in `BindableBase`. It's also supposed to be the embodiment of best practice... Is it an oversight, do you think? – toadflakz Dec 12 '14 at 10:56
  • Best practices aren't always universally agreed on :) I'd have to look carefully at the code to see whether it would be suitable. – Jon Skeet Dec 12 '14 at 11:02
  • @toadflakz PRISM isn't exactly regarded as best-practice - actually it's considered to be over-overengineered, infrequently updated and its View-first approach makes implementing MVVM harder. The major MVVM frameworks like MVVM Light or Caliburn.Micro *do* use expressions and CallerMemberName – Panagiotis Kanavos Dec 12 '14 at 11:09
1

I don't know if you're using INotifyPropertyChanged but there's some articles on how to avoid using "magic strings" here which may be of use:

Implementing NotifyPropertyChanged without magic strings

typesafe NotifyPropertyChanged using linq expressions

Community
  • 1
  • 1
Tim Rutter
  • 4,549
  • 3
  • 23
  • 47
0

Check out statically typed reflection with LINQ: http://blogs.clariusconsulting.net/kzu/statically-typed-reflection-with-linq/

You can do:

string propertyName = Reflect<foo>.GetProperty(x => x.Val).Name;
Erti-Chris Eelmaa
  • 25,338
  • 6
  • 61
  • 78