繁体   English   中英

当属性已知时,为什么不使用动态而不是反射呢?

[英]Why not use `dynamic` instead of reflection when the property is known?

这个问题与类似,但是假设我们在编译时知道成员名称。


假设我们有一堂课

public class MyClass
{
    public string TheProperty { get; set; }
}

在另一种方法中,我们想设置该类实例的TheProperty成员,但是在编译时我们不知道实例的类型,而在编译时我们只知道属性名称。 因此,正如我所看到的,现在有两种方法可以做到这一点:

object o = new MyClass(); // For simplicity.

o.GetType().GetProperty("TheProperty").SetValue(o, "bar"); // (1)    
((dynamic) o).TheProperty = "bar"; // (2)

我使用System.Diagnostics.Stopwatch类测量了该测试用例,以发现反射花费了475个滴答声 ,而使用dynamic花费了0个滴答声 ,因此与直接调用new MyClass().TheProperty = "bar"一样快new MyClass().TheProperty = "bar"


由于我几乎从未见过第二种方法,因此我有些困惑,现在我的问题是:

  • 是否有思想流失或其他?
  • 应该采用第二种方法而不是第一种或另一种方法吗? 我看不到使用第二种方法的任何缺点。 如果找不到该属性,则(1)和(2)都将引发异常,不是吗?
  • 为什么第二种方法尽管看起来更快,却似乎很少使用?

(...)反射花费475个滴答声,而使用动态的方式花费0个滴答声(...)

那是完全错误的。 问题在于您不了解dynamic工作原理。 我将假设您正确设置了基准:

  1. 在发布模式下运行并启用了优化功能,并且没有调试器。
  2. 您在实际测量时间之前将这些方法设置为吉时。

这是您可能没有做的关键部分:

  1. 无需实际执行动态运行时绑定即可启动动态测试。

为什么3很重要? 因为运行时将缓存动态调用并重用它! 因此,在幼稚的基准测试实现中,如果您做得正确,则会招致初始动态调用(使该方法无效)的开销,因此您将无法进行度量。

运行以下基准:

public static void Main(string[] args)
{
    var repetitions = 1;
    var isWarmup = true;
    var foo = new Foo();

    //warmup
    SetPropertyWithDynamic(foo, isWarmup); //JIT method without caching the dynamic call
    SetPropertyWithReflection(foo); //JIT method
    var s = ((dynamic)"Hello").Substring(0, 2); //Start up the runtime compiler

    for (var test = 0; test < 10; test++)
    {
        Console.WriteLine($"Test #{test}");
        var watch = Stopwatch.StartNew();

        for (var i = 0; i < repetitions; i++)
        {
            SetPropertyWithDynamic(foo);
        }

        watch.Stop();
        Console.WriteLine($"Dynamic benchmark: {watch.ElapsedTicks}");

        watch = Stopwatch.StartNew();

        for (var i = 0; i < repetitions; i++)
        {
            SetPropertyWithReflection(foo);
        }

        watch.Stop();
        Console.WriteLine($"Reflection benchmark: {watch.ElapsedTicks}");
    }

    Console.WriteLine(foo);
    Console.ReadLine();
}

static void SetPropertyWithDynamic(object o, bool isWarmup = false)
{
    if (isWarmup)
        return;

    ((dynamic)o).TheProperty = 1;
}

static void SetPropertyWithReflection(object o)
{
    o.GetType().GetProperty("TheProperty").SetValue(o, 1);
}

public class Foo
{
    public int TheProperty { get; set; }
    public override string ToString() => $"Foo: {TheProperty}";
}

找出第一次运行与后续运行之间的区别?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM