1

I have a program that reads a json file with some property names of a certain class. The values of the configured property names should compose up a key.

Lets take an example:

Class:

class SomeClass
{
    public string PropertyOne { get; set; }
    public string PropertyTwo { get; set; }
    public string PropertyThree { get; set; }
    public string PropertyFour { get; set; }
}

var someClass = new SomeClass
            {
                PropertyOne = "Value1",
                PropertyTwo = "Value2",
                PropertyThree = "Value3",
                PropertyFour = "Value4",
            };

Configuration file:

{
   "Properties": ["PropertyOne", "PropertyTwo"]
}

If I knew the properties at compile time I would have create a lambda like:

Func<SomeClass, string> keyFactory = x => $"{x.PropertyOne}|{x.PropertoTwo}"

Is there a way to compile such a lambda using expressions? Or any other suggestions maybe?

5

1 Answer 1

1

In Expression Tree, string interpolation is converted to string.Format. Analogue of your sample will be:

Func<SomeClass, string> keyFactory = 
   x => string.Format("{0}|{1}", x.PropertyOne, x.PropertoTwo);

The following function created such delegate dynamically:

private static MethodInfo _fromatMethodInfo = typeof(string).GetMethod(nameof(string.Format), new Type[] { typeof(string), typeof(object[]) });

public static Func<T, string> GenerateKeyFactory<T>(IEnumerable<string> propertyNames)
{
    var entityParam = Expression.Parameter(typeof(T), "e");

    var args = propertyNames.Select(p => (Expression)Expression.PropertyOrField(entityParam, p))
            .ToList();

    var formatStr = string.Join('|', args.Select((_, idx) => $"{{{idx}}}"));

    var argsParam = Expression.NewArrayInit(typeof(object), args);

    var body = Expression.Call(_fromatMethodInfo, Expression.Constant(formatStr), argsParam);
    var lambda = Expression.Lambda<Func<T, string>>(body, entityParam);
    var compiled = lambda.Compile();

    return compiled;
}

Usage:

var keyFactory = GenerateKeyFactory<SomeClass>(new[] { "PropertyOne", "PropertyTwo", "PropertyThree" });
Sign up to request clarification or add additional context in comments.

6 Comments

Looks promising! However, I am getting an exception on the Expression.Call; System.InvalidOperationException: 'No method 'Format' on type 'System.String' is compatible with the supplied arguments.' I suppose the Format method is called with Format(params object[]) and not Format(string, params object[])
Will check. Tested with .net6. Which .net do you use?
I am using .net 5
Check updated version.
Thank you! Your solution is working. I just had to make some minor changes :).
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.