c# 点运算符开销:什么更有效率
c# dot operator overhead: What is more efficient
所以我有一个 WPF 应用程序,它有一个带有子 MVVM 的基本 MVVM。我尝试用谷歌搜索答案,但不确定技术术语,所以我将在下面提供两个示例,也许有人可以让我对这些示例的效率有一些了解。我想知道在开销上是差别不大还是显着。
假设我有一个类似于以下的设置
public class ParentViewModel
{
public ParentViewModel()
{
Child = new ChildViewModel();
}
public ChildViewModel Child { get; set; }
}
public class ChildViewModel
{
public ChildViewModel()
{
GrandChild = new GrandChildViewModel();
}
public GrandChildViewModel GrandChild { get; set; }
}
public class GrandChildViewModel
{
public GrandChildViewModel()
{
GreatGrandChild = new GreatGrandChildViewModel();
}
public GreatGrandChildViewModel GreatGrandChild { get; set; }
}
public class GreatGrandChildViewModel
{
public GreatGrandChildViewModel()
{
intA = 1;
intB = 2;
intC = 3;
}
public int intA { get; set; }
public int intB { get; set; }
public int intC { get; set; }
}
以下两个用法示例是我想要了解的地方。
示例 1:
public Main()
{
var parent = new ParentViewModel();
Console.WriteLine($"A: {parent.Child.GrandChild.GreatGrandChild.intA}" +
$"B: {parent.Child.GrandChild.GreatGrandChild.intB}" +
$"C: {parent.Child.GrandChild.GreatGrandChild.intC}");
}
示例 2:
public Main()
{
var greatGrandChild = new ParentViewModel().Child.GrandChild.GreatGrandChild;
Console.WriteLine($"A: {greatGrandChild.intA}" +
$"B: {greatGrandChild.intB}" +
$"C: {greatGrandChild.intC}");
}
哪个效率更高?我之所以这样问,是因为我认为示例 2 会更有效率,因为它下降到最低级别一次,然后访问 intA、intB 和 intC。
这有关系吗?性能差异显着吗?
您会注意到两者之间完全没有优化。事实上,我怀疑编译器会将这两种类型的语句优化为同一个 IL。
不过,后一个示例更具可读性,所以我会选择这种方法。
我建议您购买您需要的最小物品。
在您给出的示例中,性能差异可以忽略不计,但是如果 parent/grandparent/greatgrandparent 对象中恰好有更多数据,并且您正在传递该对象(尤其是通过网络) ,那么它可能会有所作为。想象一下,将某人的整个家谱对象传递给某个真正只需要此人姓名的 Web 服务。
但它也通过抓住您需要的最小物体来表明您的意图。有意识的编程通常更容易阅读和维护,并让您更容易找到错误。
虽然最初的想法是编译器会针对相同的 IL 进行优化,但这显然不是真的
虽然我没有检查 IL,但快速而肮脏的秒表测试表明孙子路线要快得多
使用问题中的视图模型,结果如下:
var parent = new ParentViewModel();
var greatGrandChild = new ParentViewModel().Child.GrandChild.GreatGrandChild;
var watch = new Stopwatch();
var a = parent.Child.GrandChild.GreatGrandChild.intA;
var b = parent.Child.GrandChild.GreatGrandChild.intB;
var c = parent.Child.GrandChild.GreatGrandChild.intC;
var bothTotal = 0L;
var longTotal = 0L;
var shortTotal = 0L;
watch.Start();
a = parent.Child.GrandChild.GreatGrandChild.intA;
b = parent.Child.GrandChild.GreatGrandChild.intB;
c = parent.Child.GrandChild.GreatGrandChild.intC;
a = greatGrandChild.intA;
b = greatGrandChild.intB;
c = greatGrandChild.intC;
watch.Stop();
bothTotal += watch.ElapsedTicks;
watch.Reset();
Console.WriteLine("Longhand and Shorthand: " + bothTotal);
watch.Start();
a = parent.Child.GrandChild.GreatGrandChild.intA;
b = parent.Child.GrandChild.GreatGrandChild.intB;
c = parent.Child.GrandChild.GreatGrandChild.intC;
a = parent.Child.GrandChild.GreatGrandChild.intA;
b = parent.Child.GrandChild.GreatGrandChild.intB;
c = parent.Child.GrandChild.GreatGrandChild.intC;
watch.Stop();
longTotal += watch.ElapsedTicks;
watch.Reset();
Console.WriteLine("Longhand Only: " + longTotal);
watch.Start();
a = greatGrandChild.intA;
b = greatGrandChild.intB;
c = greatGrandChild.intC;
a = greatGrandChild.intA;
b = greatGrandChild.intB;
c = greatGrandChild.intC;
watch.Stop();
shortTotal += watch.ElapsedTicks;
watch.Reset();
Console.WriteLine("Shorthand Only: " + shortTotal);
这些是我的典型结果:
Longhand and Shorthand: 22
Longhand Only: 3
Shorthand Only: 2
这显然不是最细化的测试,并且肯定有一些值得争论的事情倾向于确认偏差 - 但从表面上看,'shorthand' 路线似乎更优化,而且值得进一步测试
更新
我检查了生成的 IL,结果很清楚:
手写:
// Setup the object:
newobj instance void ParentViewModel::.ctor()
// Actually call the members:
callvirt instance class ChildViewModel ParentViewModel::get_Child()
callvirt instance class GrandChildViewModel ChildViewModel::get_GrandChild()
callvirt instance class GreatGrandChildViewModel
GrandChildViewModel::get_GreatGrandChild()
callvirt instance int32 GreatGrandChildViewModel::get_intA()
callvirt instance class ChildViewModel ParentViewModel::get_Child()
callvirt instance class GrandChildViewModel ChildViewModel::get_GrandChild()
callvirt instance class GreatGrandChildViewModel
GrandChildViewModel::get_GreatGrandChild()
callvirt instance int32 GreatGrandChildViewModel::get_intB()
callvirt instance class ChildViewModel ParentViewModel::get_Child()
callvirt instance class GrandChildViewModel ChildViewModel::get_GrandChild()
callvirt instance class GreatGrandChildViewModel
GrandChildViewModel::get_GreatGrandChild()
callvirt instance int32 GreatGrandChildViewModel::get_intC()
Shorthand:
// Setup the object
newobj instance void ParentViewModel::.ctor()
call instance class ChildViewModel ParentViewModel::get_Child()
callvirt instance class GrandChildViewModel ChildViewModel::get_GrandChild()
callvirt instance class GreatGrandChildViewModel GrandChildViewModel::get_GreatGrandChild()
// Actually call the members:
callvirt instance int32 GreatGrandChildViewModel::get_intA()
callvirt instance int32 GreatGrandChildViewModel::get_intB()
callvirt instance int32 GreatGrandChildViewModel::get_intC()
所以我有一个 WPF 应用程序,它有一个带有子 MVVM 的基本 MVVM。我尝试用谷歌搜索答案,但不确定技术术语,所以我将在下面提供两个示例,也许有人可以让我对这些示例的效率有一些了解。我想知道在开销上是差别不大还是显着。
假设我有一个类似于以下的设置
public class ParentViewModel
{
public ParentViewModel()
{
Child = new ChildViewModel();
}
public ChildViewModel Child { get; set; }
}
public class ChildViewModel
{
public ChildViewModel()
{
GrandChild = new GrandChildViewModel();
}
public GrandChildViewModel GrandChild { get; set; }
}
public class GrandChildViewModel
{
public GrandChildViewModel()
{
GreatGrandChild = new GreatGrandChildViewModel();
}
public GreatGrandChildViewModel GreatGrandChild { get; set; }
}
public class GreatGrandChildViewModel
{
public GreatGrandChildViewModel()
{
intA = 1;
intB = 2;
intC = 3;
}
public int intA { get; set; }
public int intB { get; set; }
public int intC { get; set; }
}
以下两个用法示例是我想要了解的地方。
示例 1:
public Main()
{
var parent = new ParentViewModel();
Console.WriteLine($"A: {parent.Child.GrandChild.GreatGrandChild.intA}" +
$"B: {parent.Child.GrandChild.GreatGrandChild.intB}" +
$"C: {parent.Child.GrandChild.GreatGrandChild.intC}");
}
示例 2:
public Main()
{
var greatGrandChild = new ParentViewModel().Child.GrandChild.GreatGrandChild;
Console.WriteLine($"A: {greatGrandChild.intA}" +
$"B: {greatGrandChild.intB}" +
$"C: {greatGrandChild.intC}");
}
哪个效率更高?我之所以这样问,是因为我认为示例 2 会更有效率,因为它下降到最低级别一次,然后访问 intA、intB 和 intC。 这有关系吗?性能差异显着吗?
您会注意到两者之间完全没有优化。事实上,我怀疑编译器会将这两种类型的语句优化为同一个 IL。
不过,后一个示例更具可读性,所以我会选择这种方法。
我建议您购买您需要的最小物品。
在您给出的示例中,性能差异可以忽略不计,但是如果 parent/grandparent/greatgrandparent 对象中恰好有更多数据,并且您正在传递该对象(尤其是通过网络) ,那么它可能会有所作为。想象一下,将某人的整个家谱对象传递给某个真正只需要此人姓名的 Web 服务。
但它也通过抓住您需要的最小物体来表明您的意图。有意识的编程通常更容易阅读和维护,并让您更容易找到错误。
虽然最初的想法是编译器会针对相同的 IL 进行优化,但这显然不是真的
虽然我没有检查 IL,但快速而肮脏的秒表测试表明孙子路线要快得多
使用问题中的视图模型,结果如下:
var parent = new ParentViewModel();
var greatGrandChild = new ParentViewModel().Child.GrandChild.GreatGrandChild;
var watch = new Stopwatch();
var a = parent.Child.GrandChild.GreatGrandChild.intA;
var b = parent.Child.GrandChild.GreatGrandChild.intB;
var c = parent.Child.GrandChild.GreatGrandChild.intC;
var bothTotal = 0L;
var longTotal = 0L;
var shortTotal = 0L;
watch.Start();
a = parent.Child.GrandChild.GreatGrandChild.intA;
b = parent.Child.GrandChild.GreatGrandChild.intB;
c = parent.Child.GrandChild.GreatGrandChild.intC;
a = greatGrandChild.intA;
b = greatGrandChild.intB;
c = greatGrandChild.intC;
watch.Stop();
bothTotal += watch.ElapsedTicks;
watch.Reset();
Console.WriteLine("Longhand and Shorthand: " + bothTotal);
watch.Start();
a = parent.Child.GrandChild.GreatGrandChild.intA;
b = parent.Child.GrandChild.GreatGrandChild.intB;
c = parent.Child.GrandChild.GreatGrandChild.intC;
a = parent.Child.GrandChild.GreatGrandChild.intA;
b = parent.Child.GrandChild.GreatGrandChild.intB;
c = parent.Child.GrandChild.GreatGrandChild.intC;
watch.Stop();
longTotal += watch.ElapsedTicks;
watch.Reset();
Console.WriteLine("Longhand Only: " + longTotal);
watch.Start();
a = greatGrandChild.intA;
b = greatGrandChild.intB;
c = greatGrandChild.intC;
a = greatGrandChild.intA;
b = greatGrandChild.intB;
c = greatGrandChild.intC;
watch.Stop();
shortTotal += watch.ElapsedTicks;
watch.Reset();
Console.WriteLine("Shorthand Only: " + shortTotal);
这些是我的典型结果:
Longhand and Shorthand: 22
Longhand Only: 3
Shorthand Only: 2
这显然不是最细化的测试,并且肯定有一些值得争论的事情倾向于确认偏差 - 但从表面上看,'shorthand' 路线似乎更优化,而且值得进一步测试
更新
我检查了生成的 IL,结果很清楚:
手写:
// Setup the object:
newobj instance void ParentViewModel::.ctor()
// Actually call the members:
callvirt instance class ChildViewModel ParentViewModel::get_Child()
callvirt instance class GrandChildViewModel ChildViewModel::get_GrandChild()
callvirt instance class GreatGrandChildViewModel
GrandChildViewModel::get_GreatGrandChild()
callvirt instance int32 GreatGrandChildViewModel::get_intA()
callvirt instance class ChildViewModel ParentViewModel::get_Child()
callvirt instance class GrandChildViewModel ChildViewModel::get_GrandChild()
callvirt instance class GreatGrandChildViewModel
GrandChildViewModel::get_GreatGrandChild()
callvirt instance int32 GreatGrandChildViewModel::get_intB()
callvirt instance class ChildViewModel ParentViewModel::get_Child()
callvirt instance class GrandChildViewModel ChildViewModel::get_GrandChild()
callvirt instance class GreatGrandChildViewModel
GrandChildViewModel::get_GreatGrandChild()
callvirt instance int32 GreatGrandChildViewModel::get_intC()
Shorthand:
// Setup the object
newobj instance void ParentViewModel::.ctor()
call instance class ChildViewModel ParentViewModel::get_Child()
callvirt instance class GrandChildViewModel ChildViewModel::get_GrandChild()
callvirt instance class GreatGrandChildViewModel GrandChildViewModel::get_GreatGrandChild()
// Actually call the members:
callvirt instance int32 GreatGrandChildViewModel::get_intA()
callvirt instance int32 GreatGrandChildViewModel::get_intB()
callvirt instance int32 GreatGrandChildViewModel::get_intC()