C#-如何使用反射改进代码

时间:2018-07-26 10:06:40

标签: c# reflection

我编写了一种方法,该方法将对象的所有给定属性简单地复制到具有相同类型的另一个对象中。之所以使用这种方法,是因为我不想手动定义当一个类具有100+时要复制的属性(希望这永远不会发生,但是如果...)。

    /// <summary>
    /// Copies the values of the given parameters from source to target
    /// Important Info: Works only with Properties, not with Fields
    /// </summary>
    /// <typeparam name="T">The Classtype</typeparam>
    /// <param name="target">The object the values are copied to</param>
    /// <param name="source">The object the values come from</param>
    /// <param name="properties">The Array containing the names of properties which shall be copied</param>
    private static void CopyParams<T>(T target, T source, params string[] properties)
    {
        foreach (var property in properties)
        {
            target.GetType().GetProperty(property)?.SetValue(target, source.GetType().GetProperty(property)?.GetValue(source));
        }
    }

但是,因为这在循环内使用了反射,所以速度非常慢。使用1.000.000对象和2个属性,最多需要2秒钟。如果我手动执行此操作,则需要36毫秒。有没有办法改善性能?

编辑1

有些人要求提供对象的代码,这里是:

public class TestModel
{
    public string Name { get; set; }

    public int Value { get; set; }

    public void GetValues(TestModel m)
    {
        Name = m.Name;
        Value = m.Value;
    }
}

代码被这样调用:

    private static void PerformanceTestReflection(int count)
    {
        var models = new List<TestModel>();
        var copies = new List<TestModel>();

        for (int i = 0; i < count; i++)
        {
            models.Add(new TestModel() { Name = "original", Value = 10 });
            copies.Add(new TestModel() { Name = "copy", Value = 20 });
        }

        Stopwatch sw = Stopwatch.StartNew();

        for (int i = 0; i < count; i++)
        {
            CopyParams(models[i], copies[i], nameof(TestModel.Name), nameof(TestModel.Value));
        }

        Console.WriteLine($"Time for Reflection with {count} Models: {sw.ElapsedMilliseconds} ms - {sw.ElapsedTicks} ticks");
    }

1 个答案:

答案 0 :(得分:2)

可以使用表达式树来做某事...可以通过三种方法来实现:为每个属性创建一个表达式,为每个属性组合创建一个表达式,为每个包含大{{ 1}}(在这种情况下,foreach (var property in properties) switch (property) { case "Prop1": target.Prop1 = source.Prop1; break; ... } }不使用反射)。

我将做最简单的第一个

target.Prop1 = source.Prop1

这里有“一个属性==一个表达式树”的代码。为“一组属性==一个表达式树”执行此操作稍微复杂一点,因为您需要public static class Tools { public static void CopyFrom<T>(this T target, T source, params string[] properties) { ToolsImpl<T>.CopyFrom(target, source, properties); } private static class ToolsImpl<T> { private static readonly ConcurrentDictionary<string, Action<T, T>> delegates = new ConcurrentDictionary<string, Action<T, T>>(); public static void CopyFrom(T target, T source, string[] properties) { foreach (var property in properties) { Action<T, T> del; if (!delegates.TryGetValue(property, out del)) { var t2 = Expression.Parameter(typeof(T), "t"); var s2 = Expression.Parameter(typeof(T), "s"); var prop = typeof(T).GetProperty(property); // The ?. in the source: skip missing properties if (prop == null) { continue; } Expression<Action<T, T>> exp = Expression.Lambda<Action<T, T>>(Expression.Assign(Expression.Property(t2, prop), Expression.Property(s2, prop)), t2, s2); del = exp.Compile(); delegates.TryAdd(property, del); } del(target, source); } } } } 的比较器。第三个更加复杂(您有string[] / for在表达式树中不存在,因此必须构建,而foreach总是很痛苦)

与(编译的)表达式树一样,第一次运行的速度和狗一样慢,然后变得更快。

附录

出于好奇,第三种方式(模拟switch周期+ for):

switch