简体   繁体   English

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

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

This question is similar to this one , but assuming that we know the member name at compile time. 这个问题与类似,但是假设我们在编译时知道成员名称。


Assuming that we have a class 假设我们有一堂课

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

and in another method, we want to set the TheProperty member of an instance of that class, but we don't know the type of the instance at compile time, we only know the property name at compile time. 在另一种方法中,我们想设置该类实例的TheProperty成员,但是在编译时我们不知道实例的类型,而在编译时我们只知道属性名称。 So, as I see it, there are two ways to do that now: 因此,正如我所看到的,现在有两种方法可以做到这一点:

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

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

I measured this test case using the System.Diagnostics.Stopwatch class to find out that reflection took 475 ticks and the way using dynamic took 0 ticks , therefore being about as fast as a direct call to new MyClass().TheProperty = "bar" . 我使用System.Diagnostics.Stopwatch类测量了该测试用例,以发现反射花费了475个滴答声 ,而使用dynamic花费了0个滴答声 ,因此与直接调用new MyClass().TheProperty = "bar"一样快new MyClass().TheProperty = "bar"


Since I have almost never seen the second way, I am a little confused and my questions now are: 由于我几乎从未见过第二种方法,因此我有些困惑,现在我的问题是:

  • Is there a lapse of thought or anything? 是否有思想流失或其他?
  • Should the second way be preferred over the first or the other way around? 应该采用第二种方法而不是第一种或另一种方法吗? I don't see any disadvantages of using the second way; 我看不到使用第二种方法的任何缺点。 both (1) and (2) would throw exceptions if the property would not have been found, wouldn't they? 如果找不到该属性,则(1)和(2)都将引发异常,不是吗?
  • Why does the second way seem to be used so rarely even though seemingly being the faster? 为什么第二种方法尽管看起来更快,却似乎很少使用?

(...)reflection took 475 ticks and the way using dynamic took 0 ticks(...) (...)反射花费475个滴答声,而使用动态的方式花费0个滴答声(...)

That is simply false. 那是完全错误的。 The problem is that you are not understanding how dynamic works. 问题在于您不了解dynamic工作原理。 I will assume you are correctly setting up the benchmark: 我将假设您正确设置了基准:

  1. Running in Release mode with optimizations turned on and without the debugger. 在发布模式下运行并启用了优化功能,并且没有调试器。
  2. You are jitting the methods before actually measuring times. 您在实际测量时间之前将这些方法设置为吉时。

And here comes the key part you are probably not doing: 这是您可能没有做的关键部分:

  1. Jit the dynamic test without actually performing the dynamic runtime binding . 无需实际执行动态运行时绑定即可启动动态测试。

And why is 3 important? 为什么3很重要? Because the runtime will cache the dynamic call and reuse it! 因为运行时将缓存动态调用并重用它! So in a naive benchmark implementation, if you are doing things right, you will incurr the cost of the initial dynamic call jitting the method and therefore you won't measure it. 因此,在幼稚的基准测试实现中,如果您做得正确,则会招致初始动态调用(使该方法无效)的开销,因此您将无法进行度量。

Run the following benchmark: 运行以下基准:

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}";
}

Spot the difference between the first run and the subsequent ones? 找出第一次运行与后续运行之间的区别?

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

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