如何设置值属性选择器Expression <Func >

我需要使用模式工厂的想法将我的Person类实体中的实体属性Address与我的FactoryEntities类中的表达式linq相关联,看看这就是我拥有的和我想做的事情:

Address address = new Address(); address.Country = "Chile"; address.City = "Santiago"; address.ZipCode = "43532"; //Factory instance creation object //This is idea Person person = new FactoryEntity().AssociateWithEntity(p=>p.Address, address); public class Person: Entity { public string Name{ get; set; } public string LastName{ get; set; } public Address Address{ get; set; } } public class Address: Entity { public string Country{ get; set; } public string City{ get; set; } public string ZipCode{ get; set; } } public class FactoryEntity where TEntity : Entity { public void AssociateWithEntity(Expression<Func> entityExpression, TProperty newValueEntity) where TProperty : Entity { if (instanceEntity == null || instanceEntity.IsTransient()) throw new ArgumentNullException(); /*TODO: Logic the association and validation How set the newValueEntity into the property of entityExpression (x=>x.Direccion = direccion*/ } } 

这有效:

以下帮助器方法将getter表达式转换为setter委托。 如果要返回Expression>而不是Action ,则不要在最后调用Compile()方法。

注意:代码来自Ian Mercer的博客: http : //blog.abodit.com/2011/09/convert-a-property-getter-to-a-setter/

  ///  /// Convert a lambda expression for a getter into a setter ///  public static Action GetSetter(Expression> expression) { var memberExpression = (MemberExpression)expression.Body; var property = (PropertyInfo)memberExpression.Member; var setMethod = property.GetSetMethod(); var parameterT = Expression.Parameter(typeof(T), "x"); var parameterTProperty = Expression.Parameter(typeof(TProperty), "y"); var newExpression = Expression.Lambda>( Expression.Call(parameterT, setMethod, parameterTProperty), parameterT, parameterTProperty ); return newExpression.Compile(); } 

您可以像这样设置属性:

 public void AssociateWithEntity( Expression> entityExpression, TProperty newValueEntity) where TProperty : Entity { if (instanceEntity == null) throw new ArgumentNullException(); var memberExpression = (MemberExpression)entityExpression.Body; var property = (PropertyInfo)memberExpression.Member; property.SetValue(instanceEntity, newValueEntity, null); } 

这仅适用于属性,而不适用于字段,尽管添加对字段的支持应该很容易。

但是你获得这个人的代码是行不通的。 如果你想保留AssociateWithEntity()void返回类型,你可以这样做:

 var factory = new FactoryEntity(); factory.AssociateWithEntity(p => p.Address, address); Person person = factory.InstanceEntity; 

另一种选择是流畅的界面:

 Person person = new FactoryEntity() .AssociateWithEntity(p => p.Address, address) .InstanceEntity; 

另一个解决方案是使用reflection获取属性所有者ant invoke属性setter。 此解决方案的优点是它不使用扩展方法,可以使用任何类型调用

 private void SetPropertyValue(Expression> lambda, object value) { var memberExpression = (MemberExpression)lambda.Body; var propertyInfo = (PropertyInfo)memberExpression.Member; var propertyOwnerExpression = (MemberExpression)memberExpression.Expression; var propertyOwner = Expression.Lambda(propertyOwnerExpression).Compile().DynamicInvoke(); propertyInfo.SetValue(propertyOwner, value, null); } ... SetPropertyValue(s => myStuff.MyPropy, newValue); 

这就是我的想法,我为这个代码工作,考虑到svick的贡献:

 public class FactoryEntity where TEntity : Entity, new() { private TEntity _Entity; public FactoryEntity() { _Entity = new TEntity(); } public TEntity Build() { if (_Entity.IsValid()) throw new Exception("_Entity.Id"); return _Entity; } public FactoryEntity AssociateWithEntity(Expression> foreignEntity, TProperty instanceEntity) where TProperty : Entity { if (instanceEntity == null || instanceEntity.IsTransient()) throw new ArgumentNullException(); SetObjectValue(_Entity, foreignEntity, instanceEntity); return this; } private void SetObjectValue(object target, Expression> expression, TResult value) { var memberExpression = (MemberExpression)expression.Body; var propertyInfo = (PropertyInfo)memberExpression.Member; var newValue = Convert.ChangeType(value, value.GetType()); propertyInfo.SetValue(target, newValue, null); } } 

在这里,我调用工厂为我构建一个有效的Person对象

 Person person = new FactoryEntity().AssociateWithEntity(p=>p.Address, address).Build(); 

但是我不知道这段代码是否是最优的,至少我没有调用compile()方法,这是什么意思?

谢谢

我已经制作了混合的Rytis I解决方案和https://stackoverflow.com/a/12423256/254109

 private static void SetPropertyValue(Expression> lambda, object value) { var memberExpression = (MemberExpression)lambda.Body; var propertyInfo = (PropertyInfo)memberExpression.Member; var propertyOwnerExpression = (MemberExpression)memberExpression.Expression; var propertyOwner = Expression.Lambda(propertyOwnerExpression).Compile().DynamicInvoke(); propertyInfo.SetValue(propertyOwner, value, null); } 

并称之为

 SetPropertyValue(() => myStuff.MyProp, newValue); 

这是我使用Expression.Assign解决方案,但仔细观察后,接受的答案也同样好。

 // optionally or additionally put in a class to capture the object type once // and then you don't have to repeat it if you have a lot of properties public Action GetSetter( Expression> pExpression ) { var parameter1 = Expression.Parameter(typeof(T)); var parameter2 = Expression.Parameter(typeof(TProperty)); // turning an expression body into a PropertyInfo is common enough // that it's a good idea to extract this to a reusable method var member = (MemberExpression)pExpression.Body; var propertyInfo = (PropertyInfo)member.Member; // use the PropertyInfo to make a property expression // for the first parameter (the object) var property = Expression.Property(parameter1, propertyInfo); // assignment expression that assigns the second parameter (value) to the property var assignment = Expression.Assign(property, parameter2); // then just build the lambda, which takes 2 parameters, and has the assignment // expression for its body var setter = Expression.Lambda>( assignment, parameter1, parameter2 ); return setter.Compile(); } 

你可以做的另一件事是封装它们:

 public sealed class StrongProperty { readonly PropertyInfo mPropertyInfo; public string Name => mPropertyInfo.Name; public Func Get { get; } public Action Set { get; } // maybe other useful properties internal StrongProperty( PropertyInfo pPropertyInfo, Func pGet, Action pSet ) { mPropertyInfo = pPropertyInfo; Get = pGet; Set = pSet; } } 

现在你可以传递这些,类似于委托,并编写逻辑可能因属性而异的代码。 这解决了您无法通过引用传递属性的事实。