Id
Name
Items
Items
Child
Child.Name
Children
Children.Select(c => c.Id).ToArray(
void Main() | |
{ | |
// from comment discussion http://stackoverflow.com/questions/671968/retrieving-property-name-from-lambda-expression/17220748#17220748 | |
Expression<Func<O, int>> exprId = o => o.Id; | |
Expression<Func<O, string>> exprName = o => o.Name; | |
Expression<Func<O, int[]>> exprItems = o => o.Items; | |
Expression<Func<O, int>> exprItemsLength = o => o.Items.Length; | |
Expression<Func<O, Subclass>> exprChild = o => o.Child; | |
Expression<Func<O, string>> exprChildName = o => o.Child.Name; | |
Expression<Func<O, Subclass[]>> exprChildren = o => o.Children; | |
Expression<Func<O, int[]>> exprChildrenIds = o => o.Children.Select(c => c.Id).ToArray(); | |
exprId.GetPropertyName().Dump(exprId.ToString()); | |
exprName.GetPropertyName().Dump(exprName.ToString()); | |
exprItems.GetPropertyName().Dump(exprItems.ToString()); | |
exprItemsLength.GetPropertyName().Dump(exprItemsLength.ToString()); | |
exprChild.GetPropertyName().Dump(exprChild.ToString()); | |
exprChildName.GetPropertyName().Dump(exprChildName.ToString()); | |
exprChildren.GetPropertyName().Dump(exprChildren.ToString()); | |
exprChildrenIds.GetPropertyName().Dump(exprChildrenIds.ToString()); | |
} | |
// Define other methods and classes here | |
public class O { | |
public int Id; | |
public string Name; | |
public int[] Items; | |
public Subclass Child; | |
public Subclass[] Children; | |
public O() { } | |
} | |
public class Subclass { | |
public int Id; | |
public string Name; | |
} | |
public static class ExprExt { | |
/// <summary> | |
/// Given an expression, extract the listed property name; similar to reflection but with familiar LINQ+lambdas. Technique @via http://stackoverflow.com/a/16647343/1037948 | |
/// </summary> | |
/// <remarks>Cheats and uses the tostring output -- Should consult performance differences</remarks> | |
/// <typeparam name="TModel">the model type to extract property names</typeparam> | |
/// <typeparam name="TValue">the value type of the expected property</typeparam> | |
/// <param name="propertySelector">expression that just selects a model property to be turned into a string</param> | |
/// <param name="delimiter">Expression toString delimiter to split from lambda param</param> | |
/// <param name="endTrim">Sometimes the Expression toString contains a method call, something like "Convert(x)", so we need to strip the closing part from the end</pa ram > | |
/// <returns>indicated property name</returns> | |
public static string GetPropertyName<TModel, TValue>(this Expression<Func<TModel, TValue>> propertySelector, char delimiter = '.', char endTrim = ')') { | |
var asString = propertySelector.ToString(); // gives you: "o => o.Whatever" | |
var firstDelim = asString.IndexOf(delimiter); // make sure there is a beginning property indicator; the "." in "o.Whatever" -- this may not be necessary? | |
return firstDelim < 0 | |
? asString | |
: asString.Substring(firstDelim+1).TrimEnd(endTrim); | |
}//-- fn GetPropertyNameExtended | |
public static string GetName(Expression<Func<object>> exp) | |
{ | |
MemberExpression body = exp.Body as MemberExpression; | |
if (body == null) { | |
UnaryExpression ubody = (UnaryExpression)exp.Body; | |
body = ubody.Operand as MemberExpression; | |
} | |
return body.Member.Name; | |
} | |
} |
void Main() | |
{ | |
// list of expressions to test against | |
var expressions = buildExpressionTestList(); | |
// now performance | |
IEnumerable<string> results; | |
"Original Method".Perf(i => { | |
results = expressions.Select(o => GetPropertyName(o)); | |
}); | |
"Member Method".Perf(i => { | |
results = expressions.Select(o => GetPropertyNameFromMemberString(o)); | |
}); | |
"ToString Method".Perf(i => { | |
results = expressions.Select(o => GetPropertyNameFromString(o)); | |
}); | |
// do the methods work as expected? | |
var originalResult = expressions.Select(o => GetPropertyName(o)); | |
var memberResult = expressions.Select(o => GetPropertyNameFromMemberString(o)); | |
var stringResult = expressions.Select(o => GetPropertyNameFromString(o)); | |
originalResult.Dump("Original Method"); | |
memberResult.Dump("Member Method"); | |
stringResult.Dump("ToString Method"); | |
if( !memberResult.Except(originalResult).Any() ) throw new Exception("Should end up with differences between the two methods"); | |
if( memberResult.Except(stringResult).Any() ) throw new Exception("Should NOT end up with differences between the two methods"); | |
} | |
// Define other methods and classes here | |
protected class Foo { | |
public Guid ID { get; set; } | |
public string Name { get; set; } | |
public Bar FooBar { get; set; } | |
} | |
protected class Bar { | |
public int IDEN { get; set; } | |
public string Classification { get; set; } | |
public ICollection<Bar> FooBars { get; set; } | |
} | |
private static List<Expression<Func<Foo, object>>> buildExpressionTestList() { | |
var expressions = new List<Expression<Func<Foo, object>>> { | |
(o) => o.ID | |
, | |
(o) => o.Name | |
, | |
(o) => o.FooBar | |
, | |
(o) => o.FooBar.IDEN | |
, | |
(o) => o.FooBar.Classification | |
//, (o) => "frankenstein" | |
}; | |
return expressions; | |
} | |
/// <summary> | |
/// Given an expression, extract the listed property name; similar to reflection but with familiar LINQ+lambdas. Technique @via http://stackoverflow.com/a/16647343/1037948 | |
/// </summary> | |
/// <remarks>Just gives the last bit of the puzzle (i.e. when o => o.Name, returns "Name"; but if o => o.Foo.Bar.Name, only returns "Name")</remarks> | |
/// <typeparam name="TModel">the model type to extract property names</typeparam> | |
/// <typeparam name="TValue">the value type of the expected property</typeparam> | |
/// <param name="propertySelector">expression that just selects a model property to be turned into a string</param> | |
/// <returns>indicated property name</returns> | |
public static string GetPropertyName<TModel, TValue>(Expression<Func<TModel, TValue>> propertySelector) { | |
var body = propertySelector.Body as MemberExpression; | |
if (body == null) { | |
var ubody = (UnaryExpression)propertySelector.Body; | |
body = ubody.Operand as MemberExpression; | |
if (body == null) { | |
throw new ArgumentException("Could not get property name", "propertySelector"); | |
} | |
} | |
return body.Member.Name; | |
}//-- fn GetPropertyName | |
// Define other methods and classes here | |
/// <summary> | |
/// Given an expression, extract the listed property name; similar to reflection but with familiar LINQ+lambdas. Technique @via http://stackoverflow.com/a/16647343/1037948 | |
/// </summary> | |
/// <remarks>Just gives the last bit of the puzzle (i.e. when o => o.Name, returns "Name"; but if o => o.Foo.Bar.Name, only returns "Name")</remarks> | |
/// <typeparam name="TModel">the model type to extract property names</typeparam> | |
/// <typeparam name="TValue">the value type of the expected property</typeparam> | |
/// <param name="propertySelector">expression that just selects a model property to be turned into a string</param> | |
/// <param name="delimiter">Expression toString delimiter to split from lambda param</param> | |
/// <returns>indicated property name</returns> | |
public static string GetPropertyNameFromMemberString<TModel, TValue>(Expression<Func<TModel, TValue>> propertySelector, char delimiter = '.') { | |
var body = propertySelector.Body as MemberExpression; | |
if (body == null) { | |
var ubody = (UnaryExpression)propertySelector.Body; | |
body = ubody.Operand as MemberExpression; | |
if (body == null) { | |
throw new ArgumentException("Could not get property name", "propertySelector"); | |
} | |
} | |
var asString = body.ToString(); | |
var firstDelim = asString.IndexOf(delimiter); | |
return firstDelim < 0 | |
? asString | |
: asString.Substring(firstDelim + 1); | |
}//-- fn GetPropertyNameFromMemberString | |
/// <summary> | |
/// Given an expression, extract the listed property name; similar to reflection but with familiar LINQ+lambdas. Technique @via http://stackoverflow.com/a/16647343/1037948 | |
/// </summary> | |
/// <remarks>Cheats and uses the tostring output -- Should consult performance differences</remarks> | |
/// <typeparam name="TModel">the model type to extract property names</typeparam> | |
/// <typeparam name="TValue">the value type of the expected property</typeparam> | |
/// <param name="propertySelector">expression that just selects a model property to be turned into a string</param> | |
/// <param name="delimiter">Expression toString delimiter to split from lambda param</param> | |
/// <param name="endTrim">Sometimes the Expression toString contains something like a "Convert" operation, so we need to strip the closing part from the end</param> | |
/// <returns>indicated property name</returns> | |
public static string GetPropertyNameFromString<TModel, TValue>(Expression<Func<TModel, TValue>> propertySelector, char delimiter = '.', char endTrim = ')') { | |
var asString = propertySelector.ToString(); | |
var firstDelim = asString.IndexOf(delimiter); | |
return firstDelim < 0 | |
? asString | |
: asString.Substring(firstDelim + 1).TrimEnd(endTrim); | |
}//-- fn GetPropertyNameFromString |
public static class MyExtensions { | |
/// <summary> Performance check -- how long do X repetitions of a task take?</summary> | |
public static long Perf(this string reportTitle, Action<int> task, int repetitions = 10000) { | |
// http://stackoverflow.com/questions/28637/is-datetime-now-the-best-way-to-measure-a-functions-performance | |
Stopwatch sw = Stopwatch.StartNew(); | |
for (int i = 0; i < repetitions; i++) { | |
task(i); | |
} | |
sw.Stop(); | |
string.Format("{0} ticks elapsed ({1} ms)", sw.Elapsed.Ticks, sw.Elapsed.TotalMilliseconds).Dump(string.Format("{0} ({1}x)", reportTitle, repetitions)); | |
return sw.Elapsed.Ticks; | |
} | |
} |
8981 ticks elapsed (0.8981 ms)
8879 ticks elapsed (0.8879 ms)
8678 ticks elapsed (0.8678 ms)
IEnumerable (5 items)
IEnumerable (5 items)
IEnumerable (5 items)