Giorgos Betsos
Giorgos Betsos

Reputation: 72165

Make properties of a class parameters to a function

My intention is to pass public properties of a class, like say:

class MyTestClass
{
    public string Name { get; set; }
    public DateTime  StartedAt { get; set; }
    public TimeSpan Duration { get; set; }
}

to a function a parameters:

static void MyCustomFunc(params Expression<Func<MyTestClass, object>>[] props)
{
    foreach (var p in props)
    {
        // The following only works for Name property, not for StartedAt or Duration
        MemberExpression member = p.Body as MemberExpression;
        PropertyInfo propertyInfo = (PropertyInfo)member.Member;

        string name = propertyInfo.Name;
        Type type = propertyInfo.PropertyType;
        Func<MyTestClass, object> func = p.Compile();
    }
}

The function is supposed to gather this info and feed it into an exporter class that exports sets of MyTestClass objects to a CSV file.

The output written to the CSV file is dependent on the number, type and order of properties fed into MyCustomFunc.

So this:

MyCustomFunc(x => x.Name, x => x.StartedAt);

produces a different result from:

MyCustomFunc(x => x.StartedAt, x => x.Name);

and

MyCustomFunc(x => x.StartedAt, x => x.Name, x => x.Duration);

is different from

MyCustomFunc(x => x.Duration, x => x.StartedAt, x => x.Name);

My problem is making the reflection work. For some reason I fail to comprehend p.Body:

The first one can be handled by

MemberExpression member = p.Body as MemberExpression;

but the second one returns null, so I get a null reference exception.

Upvotes: 2

Views: 72

Answers (1)

The business of hard-coding a type parameter made me itch a little bit, so I changed that. I had a feeling you were going to be messing with that part next. Let me know if that's not the case and I'll change it back.

public static void MyCustomFunc<T>(this T inst, params Expression<Func<T, object>>[] props)
{
    foreach (var p in props)
    {
        PropertyInfo propertyInfo = null;

        //  Because the return type of the lambda is object, when the property is a value 
        //  type, the Expression will have to be a unary expression to box the value. 
        //  The MemberExpression will be the operand from that UnaryExpression. 
        if (p.Body is UnaryExpression ue && ue.Operand is MemberExpression ueMember)
        {
            propertyInfo = (PropertyInfo)ueMember.Member;
        }
        else if (p.Body is MemberExpression member)
        {
            propertyInfo = (PropertyInfo)member.Member;
        }
        else
        {
            throw new ArgumentException("Parameters must be property access expressions " 
                + "in the form x => x.Property");
        }

        string name = propertyInfo.Name;
        Type type = propertyInfo.PropertyType;
        Func<T, object> func = p.Compile();
    }
}

Usage:

new MyTestClass { Name = "Stan", StartedAt = DateTime.Now }
    .MyCustomFunc(x => x.Name, x => x.StartedAt);

Upvotes: 4

Related Questions