vendredi 24 juillet 2020

C# Compiled expression to create new instance of T and copy values to its properties from another instance of T

I would like to know if it is possible to create an instance of a type known only at runtime and assign values to the properties of this instance by using compiled expressions and if so how it is to be done.

I have a generic class with a method which accepts an instance of T and returns a copy. T is only known at runtime or rather is user/consumer defined. I know how to do so with reflection (assuming it has an empty constructor in this example and without exception handling or null checks for simplification).

public class MyClass<T>
{
    public T CreateCopy(T source)
    {
        var type = typeof(T);
        var copy = type.GetConstructor(Type.EmptyTypes).Invoke(null);
        foreach(var pi in type.GetProperties())
        {
            pi.SetValue(copy, pi.GetValue(source));
        }
        return copy;
    }
}

Reflection is quite expensive and after some digging i found an option to at least create an instance of T with compiled expressions.

var type = typeof(T);
Expression.Lambda<Func<T>>(Expression
   .New(type.GetConstructor(Type.EmptyTypes)
       ?? throw new InvalidOperationException(
           $"Type has to have an empty public constructor. {type.Name}")))
   .Compile();

After some benchmarking i have found out that it performs around 6 times faster than the CreateCopy(...) method. The thing is that i do not know which type will be passed in as a generic and how many properties it will have. Is there a way to do all of the operations from CreateCopy(...) method with compiled expressions?

Ihave looked into Expression.Asign, Expression.MemberInit but i am not able to find anything appropriate. The problem with Expression.MemberInit ist that it expects to have an Expresssion.Bind and Expression.Constant but i cant get the values of the properties from the passed instance of T into it. Is there a way?

Thank you.

P.S. So i am looking for something like:

var type = typeof(T);
var propertyInfos = type.GetProperties();
var ctor = Expression.New(type.GetConstructor(Type.EmptyTypes));
var e = Expression.Lambda<Func<T, T>>(Expression
            .MemberInit(ctor, propertyInfos.Select(pi => 
                Expression.Bind(pi, Expression.Constant(pi.GetValue(source)))))).Compile();




Aucun commentaire:

Enregistrer un commentaire