-2

Asp.net MVC introduced the EditorFor method on the Html generic class. It allows you to write code that succinctly identifies a field of the view model. In this case the page this code is from must have a view model of some type that has a StudentId property or else this won't work.

@Html.EditorFor(m => m.StudentId)

The signature of the EditorFor property is something like this.

EditorFor<TModel,TValue>(HtmlHelper<TModel>, Expression<Func<TModel,TValue>>)

The method is defined on a generic type that knows the type of the TModel. So the lambda expression can be as simple as m => m.StudentId and we all know that the type of m is whatever TModel is. In this case it is the page's view model.

I would like to be able to write similar code, without knowing what type the property is defined on. I'd like to be able to write...

@Html.EditorFor(M.StudentId) // here M is a type not a lambda parameter
@Html.EditorFor(X.Y) // here X is a type not a lambda parameter

I'd like to be able to specify an arbitry type, and an arbitrary parameter, and have the method called with something that identifies both. For example if the method were called with a PropertyInfo then I could see both the property and the type it was defined on, both.

Said another way... In the same way that nameof(X.Y) gives a string "Y" for any arbitrary type X, I'd like an expression that gives something like a PropertyInfo too. Maybe property(X.Y) and you get back the PropertyInfo of the property Y from the type X.

  • 1
    Sample code `CallMethodWithProperty(x => x.Y);` does not demonstrate any need for an object instance... Can you please clarify what is the exact problem you are facing with that line (also review [MCVE] guidance on posting code - adding extra `var x = new X()` is totally unnecessary... instead showing definition of `CallMethodWithProperty` could improve the question - consider to [edit] it ) – Alexei Levenkov Aug 02 '21 at 19:25
  • Thanks I updated the question to clarify and demonstrate exactly what I'm asking. – John DeHope Aug 02 '21 at 19:35
  • Still don't get it: `() => X.Y` doesn't make sense *unless* you have an object, either one from the enclosing scope, or passing it through as a parameter `X => X.Y` – Charlieface Aug 02 '21 at 19:37
  • Duplicate? https://stackoverflow.com/questions/671968/retrieving-property-name-from-lambda-expression – Wiktor Zychla Aug 02 '21 at 19:41
  • @WiktorZychla Thanks but that example uses a lambda expression *with* an instance variable, in that case they have `u`. I am interested in it *without* any instance variable. – John DeHope Aug 02 '21 at 20:05
  • @JohnDeHope it is still very unclear what you want to achieve with the code you trying to write... and you have not removed unrelated `var x = new X()` line... Also with you next edit please clarify if the other way to write `Call(x => x.Y)` - `Call(X.Y)` is something you considered. (clearly it can't be what you are looking for as there is no difference between `x => x.Y` and `X.Y`) – Alexei Levenkov Aug 02 '21 at 20:06
  • 1
    OP, I think you are confused about what lambda expressions actually express. In your first example, the `x` in the lambda expression isn't the instance you created in with the `new` keyword immediately above; it's a declaration of a parameter to the expression, which becomes a *new* local variable within the expression. The instance isn't used. – John Wu Aug 02 '21 at 21:58
  • @JohnDeHope: not sure what you understand by instance here. There's lambda expression constrained by type T and the u in u => u.Prop is not an instance but a variable that binds to a T so that the lambda is possible. Why would that not fit you? Just because you want another approach? – Wiktor Zychla Aug 02 '21 at 22:00
  • @JohnWu Thank you that does help. You're right I wasn't thinking all the way through about where `x` is really defined at. So you are right I was confused about that. I am rewriting the question to make it clear what I am after. – John DeHope Aug 02 '21 at 23:03

1 Answers1

3

You can use a property expression the same way as EditorFor. You can do this without an instance.

The one difference is you'll have to specify the type of the expression's parameter to tell the compiler what the object type is. So instead of

EditorFor( x => x.Name );    //Specifies a property but not a type

...your expression would look like this:

EditorFor( (MyType x) => x.Name ); //Specifies property and the type it belongs to

You can accomplish this with a short method like this one:

static PropertyInfo GetPropertyInfo<TIn, TOut>(Expression<Func<TIn, TOut>> expression)
{
    var memberExp = expression.Body as MemberExpression;
    return memberExp?.Member as PropertyInfo;
}

And then you can do this:

var p1 = GetPropertyInfo((string s) => s.Length);
Console.WriteLine("{0}.{1}", p1.DeclaringType.FullName, p1.Name);

var p2 = GetPropertyInfo((DateTime d) => d.Minute);
Console.WriteLine("{0}.{1}", p2.DeclaringType.FullName, p2.Name);

var p3 = GetPropertyInfo((Stream s) => s.CanSeek);
Console.WriteLine("{0}.{1}", p3.DeclaringType.FullName, p3.Name);

Output:

System.String.Length
System.DateTime.Minute
System.IO.Stream.CanSeek

Notice that we never needed any instances.

John Wu
  • 50,556
  • 8
  • 44
  • 80
  • This is just about exactly what I was looking for. It'd be nice if the syntax could be a bit shorter, such as `property(X.Y)` but what you have here looks shorter than using `nameof`. Thanks! – John DeHope Aug 03 '21 at 12:04
  • From what I learned from @JohnWu I was able to dig around the internet a bit more. I found this helpful. https://www.codeproject.com/articles/1070785/get-property-names-using-lambda-expressions-in-csh – John DeHope Aug 03 '21 at 12:41