web-dev-qa-db-ja.com

プロパティセレクターの値の設定方法Expression <Func <T、TResult >>

パターンファクトリのアイデアを使用して、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*/
    }
}
26
Emilio Montes

これは機能します:

次のヘルパーメソッドは、ゲッター式をセッターデリゲートに変換します。 _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();
    }
_
28
smartcaveman

次のようにプロパティを設定できます。

_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;
_
6
svick

別の解決策は、リフレクションを使用してプロパティ所有者とプロパティセッターを呼び出すようにすることです。拡張メソッドを使用せず、任意のタイプで呼び出すことができるというこのソリューションの利点

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);
3
Rytis I

これは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;
   }
}

そして今、デリゲートと同様にこれらを渡し、ロジックがプロパティによって異なる可能性のあるコードを書くことができます。これは、参照によってプロパティを渡すことができないという事実を回避します。

1
Dave Cousineau

私は混合した 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);
0
xmedeko

それはアイデアです、私は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()メソッドを呼び出さないのですが、何を言っているのでしょうか。

ありがとう

0
Emilio Montes