今天,另一家动物工厂飞入了审查:public static class AnimalsFactory
{
public static Animal CreateAnimalByTernaryOperator(bool isCat)
{
return isCat ? (Animal)new Cat() : new Dog();
}
}
再次让我感到沮丧的是,C#迫使您将Cat对象转换为Animal。但是让种姓更好,因为通过if-else语句,代码甚至更长:public static class AnimalsFactory
{
public static Animal CreateAnimalByIfElseOperator(bool isCat)
{
if (isCat)
return new Cat();
return new Dog();
}
}
让我们离开评论一分钟,然后尝试找出答案:- 在这些示例中,IL代码会有所不同吗?
- 这些示例之一是否会对性能有所帮助?
第一个问题的答案是肯定的,IL代码不同,我将在下面显示。让我们继续讨论性能问题。下载用于BenchmarkDotNet基准测试的nuget软件包并编写测试:public class AnimalFactoryPerformanceTests
{
[ParamsAllValues]
public bool IsCat { get; set; }
[Benchmark]
public void CreateAnimalByTernaryOperator() =>
AnimalsFactory.CreateAnimalByTernaryOperator(IsCat);
[Benchmark]
public void CreateAnimalByIfElseOperator() =>
AnimalsFactory.CreateAnimalByIfElseOperator(IsCat);
}
基准结果:| | IsCat | |
|------------------------------ |------ |---------:|
| CreateAnimalByTernaryOperator | False | 1.357 ns |
| CreateAnimalByTernaryOperator | True | 1.655 ns |
|------------------------------ |------ |---------:|
| CreateAnimalByIfElseOperator | False | 1.636 ns |
| CreateAnimalByIfElseOperator | True | 1.360 ns |
令人惊讶的是,“为狗”三元的工作速度更快,而“为猫”三元的工作速度则更快。我们使用三元运算符查看该方法的IL代码:CreateAnimalByTernaryOperator(bool isCat)
{
IL_0000: ldarg.0
IL_0001: brtrue.s IL_0009
IL_0003: newobj instance void AnimalPerformance.Dog::.ctor()
IL_0008: ret
IL_0009: newobj instance void AnimalPerformance.Cat::.ctor()
IL_000e: ret
}
创建Dog对象时,将顺序执行IL_0000-IL_0008命令,而在创建Cat对象时,将发生条件跳转(IL_0001:brtrue.s IL_0009)。您可能会猜到,对于if-else语句,生成的IL代码不需要条件跳转即可创建Cat对象。通过条件分支创建Dog对象时:CreateAnimalByIfElseOperator(bool isCat)
{
IL_0000: ldarg.0
IL_0001: brfalse.s IL_0009
IL_0003: newobj instance void AnimalPerformance.Cat::.ctor()
IL_0008: ret
IL_0009: newobj instance void AnimalPerformance.Dog::.ctor()
IL_000e: ret
}
将带有switch语句的Parrot创建和新方法添加到工厂:public static class AnimalFactory
{
public static Animal CreateAnimalByTernaryOperator(AnimalType animalType)
{
return animalType == AnimalType.Cat
? new Cat()
: animalType == AnimalType.Dog
? (Animal)new Dog()
: new Parrot();
}
public static Animal CreateAnimalByIfElseOperator(AnimalType animalType)
{
if (animalType == AnimalType.Cat)
return new Cat();
if (animalType == AnimalType.Dog)
return new Dog();
return new Parrot();
}
public static Animal CreateAnimalBySwitchOperator(AnimalType animalType)
{
switch (animalType)
{
case AnimalType.Cat:
return new Cat();
case AnimalType.Dog:
return new Dog();
case AnimalType.Parrot:
return new Parrot();
default:
throw new InvalidOperationException();
}
}
}
哪种方法会更快?基准结果| | AnimalType | |
|------------------------------ |----------- |---------:|
| CreateAnimalByTernaryOperator | Cat | 2.490 ns |
| CreateAnimalByTernaryOperator | Dog | 2.515 ns |
| CreateAnimalByTernaryOperator | Parrot | 2.333 ns |
|------------------------------ |----------- |---------:|
| CreateAnimalByIfElseOperator | Cat | 2.368 ns |
| CreateAnimalByIfElseOperator | Dog | 2.545 ns |
| CreateAnimalByIfElseOperator | Parrot | 2.735 ns |
|------------------------------ |----------- |---------:|
| CreateAnimalBySwitchOperator | Cat | 2.747 ns |
| CreateAnimalBySwitchOperator | Dog | 2.730 ns |
| CreateAnimalBySwitchOperator | Parrot | 2.722 ns |
IL代码CreateAnimalByTernaryOperator(AnimalsFactory.AnimalType animalType)
{
IL_0000: ldarg.0
IL_0001: brfalse.s IL_0013
IL_0003: ldarg.0
IL_0004: ldc.i4.1
IL_0005: beq.s IL_000d
IL_0007: newobj instance void AnimalsFactory.Parrot::.ctor()
IL_000c: ret
IL_000d: newobj instance void AnimalsFactory.Dog::.ctor()
IL_0012: ret
IL_0013: newobj instance void AnimalsFactory.Cat::.ctor()
IL_0018: ret
}
CreateAnimalByIfElseOperator(AnimalsFactory.AnimalType animalType)
{
IL_0000: ldarg.0
IL_0001: brtrue.s IL_0009
IL_0003: newobj instance void AnimalsFactory.Cat::.ctor()
IL_0008: ret
IL_0009: ldarg.0
IL_000a: ldc.i4.1
IL_000b: bne.un.s IL_0013
IL_000d: newobj instance void AnimalsFactory.Dog::.ctor()
IL_0012: ret
IL_0013: newobj instance void AnimalsFactory.Parrot::.ctor()
IL_0018: ret
}
CreateOtherAnimalBySwitchOperator(AnimalsFactory.AnimalType animalType)
{
IL_0000: ldarg.0
IL_0001: switch (IL_0014, IL_001a, IL_0020)
IL_0012: br.s IL_0026
IL_0014: newobj instance void AnimalsFactory.Cat::.ctor()
IL_0019: ret
IL_001a: newobj instance void AnimalsFactory.Dog::.ctor()
IL_001f: ret
IL_0020: newobj instance void AnimalsFactory.Parrot::.ctor()
IL_0025: ret
IL_0026: newobj instance void System.InvalidOperationException::.ctor()
IL_002b: throw
}
结论1:对于三元和if-else语句,运行时直接取决于在执行线程中传递的条件跳转的数量。结论2: C#switch语句在IL代码中转换为switch语句,并且平均而言,其工作时间比常规分支语句长一点。结论3:以上所有内容仅与.NET Framework v.4.8相关。在.NetCore上运行相同的测试后,我们得到了完全不同的结果,但尚需以某种方式解释。基准.NetCore 3.0| | AnimalType | |
|------------------------------ |----------- |---------:|
| CreateAnimalByTernaryOperator | Cat | 3.046 ns |
| CreateAnimalByTernaryOperator | Dog | 2.984 ns |
| CreateAnimalByTernaryOperator | Parrot | 3.019 ns |
|------------------------------ |----------- |---------:|
| CreateAnimalByIfElseOperator | Cat | 2.980 ns |
| CreateAnimalByIfElseOperator | Dog | 2.977 ns |
| CreateAnimalByIfElseOperator | Parrot | 3.103 ns |
|------------------------------ |----------- |---------:|
| CreateAnimalBySwitchOperator | Cat | 3.519 ns |
| CreateAnimalBySwitchOperator | Dog | 3.533 ns |
| CreateAnimalBySwitchOperator | Parrot | 3.312 ns |
处理器:Intel®Core(TM)i7-7700K来源