If you don't mind writing the update code as Update(item, x => x.Property1, x=> x.Property2 )
, it would be just as readable and a bit easier to write, as shown by Johnathans answer.
Although if you add a generic, you can force x to be of the same type and reuse it for different objects besides Item
int Update<T>(T item, params Expression<Func<T, object>>[] selectors)
{
string getName(Expression e)
{
if(e is LambdaExpression l)
return getName(l.Body);
if(e is MemberExpression m)
return m.Member.Name;
if(e is UnaryExpression u)
return getName( u.Operand);
throw new NotImplementedException();
}
var names = selectors.Select(getName);
//update code...
}
NB, the helper function is a basic one, you can use a more extended reusable helper function to get the name, plenty of those around
Example code (the first anonymous object is just to create an example object):
Update(new { foo = "a", bar = 5}, x=>x.foo, x=>x.bar);
Now the funny thing is, you can either force a single expression by removing the new, or you could combine that function, and still allow anonymous, by adding a check for a NewExpression
inside the helper function (changing it to allow for multiple names of course):
int Update<T>(T item, params Expression<Func<T, object>>[] selectors)
{
IEnumerable<string> getName(Expression e)
{
if (e is LambdaExpression l)
return getName(l.Body);
if (e is MemberExpression m)
return new[] { m.Member.Name };
if (e is UnaryExpression u)
return getName(u.Operand);
if (e is NewExpression n) // <- to account for the anonymous object
return n.Arguments.SelectMany(getName);
throw new NotImplementedException();
}
var names = selectors.SelectMany(getName);
// etc
}
That way, you can choose (or even mix). Both these calls produce the same:
Update(new { foo = "a", bar = 5}, x=>x.foo, x=>x.bar);
Update(new { foo = "a", bar = 5 }, x => new { x.foo, x.bar});