这个问题类似于this one,但假设我们在编译时知道成员名称。
假设我们有一个班级
public class MyClass
{
public string TheProperty { get; set; }
}
并且在另一种方法中,我们想要设置该类实例的TheProperty
成员,但是我们在编译时不知道实例的类型,我们只知道属性名称at编译时间。
所以,正如我所看到的,现在有两种方法可以做到这一点:
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"
一样快。
由于我几乎从未见过第二种方式,我有点困惑,现在我的问题是:
答案 0 :(得分:3)
(...)反射需要475个刻度,使用动态的方式需要0个刻度(...)
这简直是假的。问题是你不了解dynamic
的工作原理。我会假设您正确设置基准:
这是你可能没有做的关键部分:
为什么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}";
}
发现第一次运行和后续运行之间的区别?