Search code examples
c#dynamicpropertychangedgenerics

Update generic property of generic class?


I'm quite new at C# Generic Classes so I don't know if we can update the generic property of a generic class dynamically?

Let say I have a class

A { long id; long count; }

Can we write an extension that update the given property by 1 so

A.AddOne(a => a.id); will change A {id=1, count=1} to A {id=2, count=1}

and

A.AddOne(a => a.count); will change A {id=1, count=1} to A {id=1, count=2}

Any helps would be appreciated!


Solution

  • If I understand your question correctly, you want to be able to declare which property to update by giving the AddOne method a lambda expression. In this case, it's possible -- you can write an extension method that takes an Expression<T>, retrieves from this expression the property access expression, and uses Reflection to update that property on the A object.

    The following is a very rough prototype of the above:

    public static void AddOne<S,T>(this S x, Expression<Func<S,T>> expr)
    {
        if (expr.Body.NodeType != ExpressionType.MemberAccess)
            throw new InvalidOperationException();
    
        MemberExpression memberExpr = expr.Body as MemberExpression;
        switch (memberExpr.Member.MemberType)
        {
            case MemberTypes.Field:
                {
                    FieldInfo field = memberExpr.Member as FieldInfo;
                    ulong value = Convert.ToUInt64(field.GetValue(x));
                    ++value;
                    field.SetValue(x, Convert.ChangeType(value, field.FieldType));
                    break;
                }
            case MemberTypes.Property:
                {
                    PropertyInfo prop = memberExpr.Member as PropertyInfo;
                    ulong value = Convert.ToUInt64(prop.GetValue(x, null));
                    ++value;
                    prop.SetValue(x, Convert.ChangeType(value, prop.PropertyType), null);
                    break;
                }
            default:
                throw new InvalidOperationException();
        }
    }