我需要使用模式工厂的想法将我的 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<Person>().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<TEntity> where TEntity : Entity
{
public void AssociateWithEntity<TProperty>(Expression<Func<TEntity, TProperty>> 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<T,TProperty>>
而不是 Action<T,TProperty>
,只需不要在最后调用 Compile()
方法即可。
注:代码来自Ian Mercer的博客:http://blog.abodit.com/2011/09/convert-a-property-getter-to-a-setter/
/// <summary>
/// Convert a lambda expression for a getter into a setter
/// </summary>
public static Action<T, TProperty> GetSetter<T, TProperty>(Expression<Func<T, TProperty>> 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<Action<T, TProperty>>(
Expression.Call(parameterT, setMethod, parameterTProperty),
parameterT,
parameterTProperty
);
return newExpression.Compile();
}
您可以这样设置属性:
public void AssociateWithEntity<TProperty>(
Expression<Func<TEntity, TProperty>> 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);
}
这仅适用于属性,不适用于字段,尽管添加对字段的支持应该很容易。
但是您用来获取此人的代码不起作用。如果你想保留
void
返回类型 AssociateWithEntity()
,你可以这样做:
var factory = new FactoryEntity<Person>();
factory.AssociateWithEntity(p => p.Address, address);
Person person = factory.InstanceEntity;
另一个选择是流畅的界面:
Person person = new FactoryEntity<Person>()
.AssociateWithEntity(p => p.Address, address)
.InstanceEntity;
另一个解决方案是获取属性所有者并使用反射调用属性设置器。这个解决方案的优点是它不使用扩展方法,并且可以用任何类型调用。
private void SetPropertyValue(Expression<Func<object, object>> 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);
这是我使用
Expression.Assign
的解决方案,但仔细观察后,接受的答案同样好。
// optionally or additionally put in a class<T> to capture the object type once
// and then you don't have to repeat it if you have a lot of properties
public Action<T, TProperty> GetSetter<T, TProperty>(
Expression<Func<T, TProperty>> 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<Action<T, TProperty>>(
assignment,
parameter1,
parameter2
);
return setter.Compile();
}
您可以做的另一件事是封装它们:
public sealed class StrongProperty<TObject, TProperty> {
readonly PropertyInfo mPropertyInfo;
public string Name => mPropertyInfo.Name;
public Func<TObject, TProperty> Get { get; }
public Action<TObject, TProperty> Set { get; }
// maybe other useful properties
internal StrongProperty(
PropertyInfo pPropertyInfo,
Func<TObject, TProperty> pGet,
Action<TObject, TProperty> pSet
) {
mPropertyInfo = pPropertyInfo;
Get = pGet;
Set = pSet;
}
}
现在您可以像委托一样传递它们,并编写逻辑可能因属性而异的代码。这解决了您无法通过引用传递属性的事实。
这就是想法,考虑到 svick 的贡献,我为我工作了这段代码:
public class FactoryEntity<TEntity> 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<TEntity> AssociateWithEntity<TProperty>(Expression<Func<TEntity, TProperty>> foreignEntity, TProperty instanceEntity) where TProperty : Entity
{
if (instanceEntity == null || instanceEntity.IsTransient())
throw new ArgumentNullException();
SetObjectValue<TEntity, TProperty>(_Entity, foreignEntity, instanceEntity);
return this;
}
private void SetObjectValue<T, TResult>(object target, Expression<Func<T, TResult>> 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<Person>().AssociateWithEntity(p=>p.Address, address).Build();
但是我不知道这段代码是否是最优的,至少我没有调用compile()方法,说什么?
谢谢
我制作了混合的 Rytis I 解决方案和 https://stackoverflow.com/a/12423256/254109
private static void SetPropertyValue<T>(Expression<Func<T>> 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);
一切都变得更加简单:
public static Action<T, TValue> GetSetter<T, TValue>(
Expression<Func<T, TValue>> expression)
{
var parameter = Expression.Parameter(typeof(TValue), "value");
var setterLambda = Expression.Lambda<Action<T, TValue>>(
Expression.Assign(expression.Body, parameter),
expression.Parameters[0],
parameter);
return setterLambda.Compile();
}
使用 ExpressionVisitor,它将遍历您的树以查找嵌套项目:
var t = new SauceObject();
t.UpdateValueAndTrack(t => t.NestedProperty.Field1, "newvalue");
Console.WriteLine(t.NestedProperty.Field1);
public static void UpdateValueAndTrack<TSourceObject, TSetValue>(this TSourceObject sourceObject, Expression<Func<TSourceObject, TSetValue>> sourceExpression, TSetValue sourceValue)
{
var t = new UpdateValueVisitor(sourceValue);
var modifiedExpression = (Expression<Func<TSourceObject, TSetValue>>)t.Visit(sourceExpression);
// Compile and execute the modified expression
var modifiedFunc = modifiedExpression.Compile();
modifiedFunc(sourceObject);
}
public class SauceObject
{
public NestedProperty NestedProperty { get; set; } = new NestedProperty();
}
public class NestedProperty
{
public string Field1 { get; set; }
public string Field2 { get; set; }
}
public class UpdateValueVisitor : ExpressionVisitor
{
private readonly object newValue;
private bool foundMember = false;
public UpdateValueVisitor(object newValue)
{
this.newValue = newValue;
}
protected override Expression VisitMember(MemberExpression node)
{
if (!foundMember && node.Member is System.Reflection.PropertyInfo propertyInfo)
{
// Update property
var newValueExpression = Expression.Constant(newValue, propertyInfo.PropertyType);
var assignment = Expression.Assign(node, newValueExpression);
foundMember = true;
return assignment;
}
return base.VisitMember(node);
}
}