gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-30 21:36:30 25 4
gpt4 key购买 nike

这个问题类似于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/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com