c# - 当属性已知时,为什么不使用 `dynamic` 而不是反射?

标签 c# dynamic reflection

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


假设我们有一个类

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" 一样快。


由于我几乎没有见过第二种方式,所以我有点困惑,现在我的问题是:

  • 有没有思路失误之类的?
  • 第二种方式应该优于第一种方式还是相反?我没有看到使用第二种方式有任何缺点;如果找不到属性,(1) 和 (2) 都会抛出异常,不是吗?
  • 为什么第二种方式看似更快,却很少被使用?

最佳答案

(...)reflection took 475 ticks and the way using dynamic took 0 ticks(...)

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

  1. 在 Release模式下运行并启用优化且没有调试器。
  2. 在实际测量时间之前,您正在放弃这些方法。

接下来是您可能没有做的关键部分:

  1. 在不实际执行动态运行时绑定(bind)的情况下停止动态测试

为什么 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}";
}

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

关于c# - 当属性已知时,为什么不使用 `dynamic` 而不是反射?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48109071/

相关文章:

ios - uitableview 之前 View 的动态 UILabel 大小

javascript - 边框半径和 Javascript

python - 如何获得谁调用了函数或方法?

Java 泛型和反射(jenkov 博客)

c# - 计算平面上封闭多边形的面积

java - 如何在Eclipse中安装Web动态模块?

c# - 防止 Entity Framework 将外键设置为 NULL

reflection - Go:反射catch22反射包

c# - 简单的 getJSON 不起作用

javascript - Xamarin 和 Telerik 平台有什么区别