如何动态地创建绑定代理?绑定、动态

2023-09-04 02:31:44 作者:毁忆大叔

我不知道如果我问是正确的,但我有点失去了好几个小时,所以裸跟我来。

我最初的问题是:我有一个配置对象,它有一个枚举,我想绑定了一堆单选的同一个属性。当我看到自己重复有时我试图想出多个项目结合到同一个属性的通用解决方案。我找到的解决方案是创建一个执行一个前pression代理。请参见下面的code。

 公开枚举GenderEnum
{
    男,
    女
}

公共类数据类
{
    公共GenderEnum性别{获得;组; }
}

一流的防爆pressionBinder< T>
{
    公共Func键< T>吸气剂;
    公益行动< T>二传手;

    公众吨价
    {
        {返回Getter.Invoke(); }
        集合{Setter.Invoke(值); }
    }
    公共防爆pressionBinder(Func键< T>吸气,动作< T>二传)
    {
        吸气=吸收剂;
        二传手=者;
    }
}
 

我可以用这种方式:

  radioMale.Tag = GenderEnum.Male;
radioFemale.Tag = GenderEnum.Female;

VAR proxyM =新的防爆pressionBinder<布尔>(
    ()=> DataObj.Gender ==(GenderEnum)radioMale.Tag,
    (VAL)=> {如果(VAL)DataObj.Gender =(GenderEnum)radioMale.Tag; });

VAR proxyF =新的防爆pressionBinder<布尔>(
    ()=> DataObj.Gender ==(GenderEnum)radioFemale.Tag,
    (VAL)=> {如果(VAL)DataObj.Gender =(GenderEnum)radioFemale.Tag; });

radioMale.DataBindings.Add(选中,proxyM,价值);
radioFemale.DataBindings.Add(选中,proxyF,价值);
 

这只是一个例子,我想保持简单。但是,这种方法实际上是和。通过问题是:

我要封装内的派生类的比较,并设置EX pressions。

 类ComparisonBinder:防爆pressionBinder {}
 
五一劳动节微信营销活动怎么做

不幸的是,我只能说我想怎么使用它。

  radioMale.DataBindings.Add(选中,
    新ComparisonBinder< ConfigClass>((三)c.Gender,GenderEnum.Male)
    值);
radioFemale.DataBindings.Add(选中,
    新ComparisonBinder< ConfigClass>((三)c.Gender,GenderEnum.Female)
    值);
 

那么,你将如何实现这一点?随意改变我的前pressionBinder类,如果你需要。

最后的code

(再次编辑:使用对象类型,你不能省略TValue参数期间的拉姆达EX pression转换的编译器将添加一个调用转换()

 类ComparisonBinder< TSource,TValue> :防爆pressionBinder<布尔>
{
    私人TSource实例;
    私人TValue comparisonValue;
    私人的PropertyInfo PINFO;

    公共ComparisonBinder(TSource例如,前pression< Func键< TSource,TValue>>财产,TValue comparisonValue)
    {
        PINFO =为getPropertyInfo(财产);

        this.instance =实例;
        this.comparisonValue = comparisonValue;

        吸气=的GetValue;
        二传手=的SetValue;
    }

    私人布尔的GetValue()
    {
        返回comparisonValue.Equals(pInfo.GetValue(例如,空));
    }

    私人无效的SetValue(布尔值)
    {
        如果(值)
        {
            pInfo.SetValue(例如,comparisonValue,NULL);
        }
    }

    ///<总结>
    ///从surfen的回答改编(http://stackoverflow.com/a/10003320/219838)
    ///< /总结>
    私人的PropertyInfo为getPropertyInfo(出pression< Func键< TSource,TValue>> propertyLambda)
    {
        类型类型= ty​​peof运算(TSource);

        MemberEx pression成员= propertyLambda.Body为MemberEx pression;

        如果(成员== NULL)
            抛出新的ArgumentException(的String.Format(
                实施例pression{0}是指一种方法,而不是一个属性。,
                propertyLambda));

        的PropertyInfo propInfo = member.Member为的PropertyInfo;
        如果(propInfo == NULL)
            抛出新的ArgumentException(的String.Format(
                前pression{0}是指一个字段,而不是一个性质。
                propertyLambda));

        如果(TYPE = propInfo.ReflectedType和放大器;!&安培;
            !type.IsSubclassOf(propInfo.ReflectedType))
            抛出新的ArgumentException(的String.Format(
                前presion公司{0}指的是一个属性,它是不是从类型{1}。,
                propertyLambda,
                类型));

        返回propInfo;
    }
}
 

用法:

  radioMale.DataBindings.Add(选中,
    新ComparisonBinder&其中;数据类,GenderEnum>(DataObj,(X)= GT; x.Gender,GenderEnum.Male),值);
radioFemale.DataBindings.Add(选中,
    新ComparisonBinder&其中;数据类,GenderEnum>(DataObj,(X)= GT; x.Gender,GenderEnum.Female),值);
 

解决方案

我觉得这可以使用的PropertyInfo 和一些拉姆达魔法来完成:

 类ComparisonBinder< TSource,TValue> :防爆pressionBinder<布尔>
{
    公共ComparisonBinder(TSource source中,前pression< Func键< TSource,布尔>> propertyLambda,TValue comparisonValue):基地(NULL,NULL)
    {
         VAR的PropertyInfo =为getPropertyInfo< TSource,布尔>(源,propertyLambda);
         this.Getter =()=> comarisonValue.Equals((TValue)propertyInfo.GetValue(源));
         this.Setter =(布尔值)=> {如果(值)propertyInfo.SetValue(来源comparisonValue); };
    }
}
 

用法:

  radioMale.DataBindings.Add(选中,
    新ComparisonBinder< ConfigClass,GenderEnum>(配置,C => c.Gender,GenderEnum.Male)
    值);
radioFemale.DataBindings.Add(选中,
    新ComparisonBinder< ConfigClass,GenderEnum>(配置,C => c.Gender,GenderEnum.Female)
    值);
 

我没有测试过,但希望你能够修复任何错误,并使用该解决方案。

为getPropertyInfo():从 http://stackoverflow.com/a/672212/724944

复制

 公开的PropertyInfo为getPropertyInfo< TSource,TProperty>(
    TSource source中,
    防爆pression< Func键< TSource,TProperty>> propertyLambda)
{
    类型类型= ty​​peof运算(TSource);

    MemberEx pression成员= propertyLambda.Body为MemberEx pression;
    如果(成员== NULL)
        抛出新的ArgumentException(的String.Format(
            实施例pression{0}是指一种方法,而不是一个属性。,
            propertyLambda.ToString()));

    的PropertyInfo propInfo = member.Member为的PropertyInfo;
    如果(propInfo == NULL)
        抛出新的ArgumentException(的String.Format(
            前pression{0}是指一个字段,而不是一个性质。
            propertyLambda.ToString()));

    如果(TYPE = propInfo.ReflectedType和放大器;!&安培;
        !type.IsSubclassOf(propInfo.ReflectedType))
        抛出新的ArgumentException(的String.Format(
            前presion公司{0}指的是一个属性,它是不是从类型{1}。,
            propertyLambda.ToString(),
            类型));

    返回propInfo;
}
 

I don't know if I asked it right, but I'm kind of lost for hours, so bare with me.

My initial problem was: I have a configuration object, which has an enum and I wanted to bind a bunch of RadioButton's to the same property. As I see myself repeating it sometimes I tried to come up with a general solution for binding multiple items to the same property. The solution I found was to create a proxy which executes an expression. See the code below.

public enum GenderEnum
{
    Male,
    Female
}

public class DataClass
{
    public GenderEnum Gender { get; set; }
}

class ExpressionBinder<T>
{
    public Func<T> Getter;
    public Action<T> Setter;

    public T Value
    {
        get { return Getter.Invoke(); }
        set { Setter.Invoke(value); }
    }
    public ExpressionBinder(Func<T> getter, Action<T> setter)
    {
        Getter = getter;
        Setter = setter;
    }
}

I can use it this way:

radioMale.Tag = GenderEnum.Male;
radioFemale.Tag = GenderEnum.Female;

var proxyM = new ExpressionBinder<bool>(
    () => DataObj.Gender == (GenderEnum)radioMale.Tag,
    (val) => { if (val) DataObj.Gender = (GenderEnum)radioMale.Tag; });

var proxyF = new ExpressionBinder<bool>(
    () => DataObj.Gender == (GenderEnum)radioFemale.Tag,
    (val) => { if (val) DataObj.Gender = (GenderEnum)radioFemale.Tag; });

radioMale.DataBindings.Add("Checked", proxyM, "Value");
radioFemale.DataBindings.Add("Checked", proxyF, "Value");

It's just an example, I wanted to keep it simple. But this approach actually WORKS. By question is:

I want to encapsulate the comparison and set expressions inside a derived class.

class ComparisonBinder : ExpressionBinder {}

Unfortunately, I can only say how I would like to use it.

radioMale.DataBindings.Add("Checked", 
    new ComparisonBinder<ConfigClass>((c) c.Gender, GenderEnum.Male), 
    "Value");
radioFemale.DataBindings.Add("Checked", 
    new ComparisonBinder<ConfigClass>((c) c.Gender, GenderEnum.Female),
    "Value");

So, how would you implement this? Feel free to change my ExpressionBinder class if you need to.

The Final code

(edited again: you can't omit the TValue parameter using object type. During the conversion of the lambda expression the compiler will add a call to Convert().)

class ComparisonBinder<TSource, TValue> : ExpressionBinder<bool>
{
    private TSource instance;
    private TValue comparisonValue;
    private PropertyInfo pInfo;

    public ComparisonBinder(TSource instance, Expression<Func<TSource, TValue>> property, TValue comparisonValue)
    {
        pInfo = GetPropertyInfo(property);

        this.instance = instance;
        this.comparisonValue = comparisonValue;

        Getter = GetValue;
        Setter = SetValue;
    }

    private bool GetValue()
    {
        return comparisonValue.Equals(pInfo.GetValue(instance, null));
    }

    private void SetValue(bool value)
    {
        if (value)
        {
            pInfo.SetValue(instance, comparisonValue,null);
        }
    }

    /// <summary>
    /// Adapted from surfen's answer (http://stackoverflow.com/a/10003320/219838)
    /// </summary>
    private PropertyInfo GetPropertyInfo(Expression<Func<TSource, TValue>> propertyLambda)
    {
        Type type = typeof(TSource);

        MemberExpression member = propertyLambda.Body as MemberExpression;

        if (member == null)
            throw new ArgumentException(string.Format(
                "Expression '{0}' refers to a method, not a property.",
                propertyLambda));

        PropertyInfo propInfo = member.Member as PropertyInfo;
        if (propInfo == null)
            throw new ArgumentException(string.Format(
                "Expression '{0}' refers to a field, not a property.",
                propertyLambda));

        if (type != propInfo.ReflectedType &&
            !type.IsSubclassOf(propInfo.ReflectedType))
            throw new ArgumentException(string.Format(
                "Expresion '{0}' refers to a property that is not from type {1}.",
                propertyLambda,
                type));

        return propInfo;
    }
}

Usage:

radioMale.DataBindings.Add("Checked", 
    new ComparisonBinder<DataClass,GenderEnum>(DataObj, (x) => x.Gender, GenderEnum.Male), "Value");
radioFemale.DataBindings.Add("Checked", 
    new ComparisonBinder<DataClass,GenderEnum>(DataObj, (x) => x.Gender, GenderEnum.Female), "Value");

解决方案

I think this can be done using PropertyInfo and some lambda magic:

class ComparisonBinder<TSource,TValue> : ExpressionBinder<bool>
{
    public ComparisonBinder(TSource source, Expression<Func<TSource, bool>> propertyLambda, TValue comparisonValue) :base(null,null)
    {
         var propertyInfo = GetPropertyInfo<TSource,bool>(source, propertyLambda);
         this.Getter = () => comarisonValue.Equals((TValue)propertyInfo.GetValue(source));
         this.Setter = (bool value) => { if(value) propertyInfo.SetValue(source, comparisonValue); };
    }
}

Usage:

radioMale.DataBindings.Add("Checked", 
    new ComparisonBinder<ConfigClass, GenderEnum>(config, c => c.Gender, GenderEnum.Male), 
    "Value");
radioFemale.DataBindings.Add("Checked", 
    new ComparisonBinder<ConfigClass, GenderEnum>(config, c => c.Gender, GenderEnum.Female),
    "Value");

I have not tested it but hopefully you'll be able to fix any errors and use this solution.

GetPropertyInfo(): copied from http://stackoverflow.com/a/672212/724944

public PropertyInfo GetPropertyInfo<TSource, TProperty>(
    TSource source,
    Expression<Func<TSource, TProperty>> propertyLambda)
{
    Type type = typeof(TSource);

    MemberExpression member = propertyLambda.Body as MemberExpression;
    if (member == null)
        throw new ArgumentException(string.Format(
            "Expression '{0}' refers to a method, not a property.",
            propertyLambda.ToString()));

    PropertyInfo propInfo = member.Member as PropertyInfo;
    if (propInfo == null)
        throw new ArgumentException(string.Format(
            "Expression '{0}' refers to a field, not a property.",
            propertyLambda.ToString()));

    if (type != propInfo.ReflectedType &&
        !type.IsSubclassOf(propInfo.ReflectedType))
        throw new ArgumentException(string.Format(
            "Expresion '{0}' refers to a property that is not from type {1}.",
            propertyLambda.ToString(),
            type));

    return propInfo;
}