Reputation: 72165
My intention is to pass public properties of a class, like say:
class MyTestClass
{
public string Name { get; set; }
public DateTime StartedAt { get; set; }
public TimeSpan Duration { get; set; }
}
to a function a parameters:
static void MyCustomFunc(params Expression<Func<MyTestClass, object>>[] props)
{
foreach (var p in props)
{
// The following only works for Name property, not for StartedAt or Duration
MemberExpression member = p.Body as MemberExpression;
PropertyInfo propertyInfo = (PropertyInfo)member.Member;
string name = propertyInfo.Name;
Type type = propertyInfo.PropertyType;
Func<MyTestClass, object> func = p.Compile();
}
}
The function is supposed to gather this info and feed it into an exporter class that exports sets of MyTestClass
objects to a CSV file.
The output written to the CSV file is dependent on the number, type and order of properties fed into MyCustomFunc
.
So this:
MyCustomFunc(x => x.Name, x => x.StartedAt);
produces a different result from:
MyCustomFunc(x => x.StartedAt, x => x.Name);
and
MyCustomFunc(x => x.StartedAt, x => x.Name, x => x.Duration);
is different from
MyCustomFunc(x => x.Duration, x => x.StartedAt, x => x.Name);
My problem is making the reflection work. For some reason I fail to comprehend p.Body
:
{x => x.Name}
is equal to {x.Name}
but{x => x.StartedAt}
is equal to {Convert(x.StartedAt)}
The first one can be handled by
MemberExpression member = p.Body as MemberExpression;
but the second one returns null
, so I get a null reference exception.
Upvotes: 2
Views: 72
Reputation: 37059
The business of hard-coding a type parameter made me itch a little bit, so I changed that. I had a feeling you were going to be messing with that part next. Let me know if that's not the case and I'll change it back.
public static void MyCustomFunc<T>(this T inst, params Expression<Func<T, object>>[] props)
{
foreach (var p in props)
{
PropertyInfo propertyInfo = null;
// Because the return type of the lambda is object, when the property is a value
// type, the Expression will have to be a unary expression to box the value.
// The MemberExpression will be the operand from that UnaryExpression.
if (p.Body is UnaryExpression ue && ue.Operand is MemberExpression ueMember)
{
propertyInfo = (PropertyInfo)ueMember.Member;
}
else if (p.Body is MemberExpression member)
{
propertyInfo = (PropertyInfo)member.Member;
}
else
{
throw new ArgumentException("Parameters must be property access expressions "
+ "in the form x => x.Property");
}
string name = propertyInfo.Name;
Type type = propertyInfo.PropertyType;
Func<T, object> func = p.Compile();
}
}
Usage:
new MyTestClass { Name = "Stan", StartedAt = DateTime.Now }
.MyCustomFunc(x => x.Name, x => x.StartedAt);
Upvotes: 4