use*_*667 8 c# enums dictionary switch-statement
在根据具体情况处理枚举值时,使用switch语句或字典会更好吗?
我认为字典会更快.就空间而言,它占用了一些内存,但case语句也会占用程序本身所需的内存中的一些内存.所以底线我认为只使用字典总是更好.
以下是两个并列的实现进行比较:
鉴于这些枚举:
enum FruitType
{
Other,
Apple,
Banana,
Mango,
Orange
}
enum SpanishFruitType
{
Otra,
Manzana, // Apple
Naranja, // Orange
Platano, // Banana
Pitaya // Dragon fruit, only grown in Mexico and South American countries, lets say
// let's say they don't have mangos, because I don't remember the word for it.
}
Run Code Online (Sandbox Code Playgroud)
以下是使用switch语句执行此操作的方法:
private static SpanishFruitType GetSpanishEquivalent(FruitType typeOfFruit)
{
switch(typeOfFruit)
{
case FruitType.Apple:
return SpanishFruitType.Manzana;
case FruitType.Banana:
return SpanishFruitType.Platano;
case FruitType.Orange:
return SpanishFruitType.Naranja;
case FruitType.Mango:
case FruitType.Other:
return SpanishFruitType.Otra;
default:
throw new Exception("what kind of fruit is " + typeOfFruit + "?!");
}
}
Run Code Online (Sandbox Code Playgroud)
这是如何用字典完成的:
private static Dictionary<FruitType, SpanishFruitType> EnglishToSpanishFruit = new Dictionary<FruitType, SpanishFruitType>()
{
{FruitType.Apple, SpanishFruitType.Manzana}
,{FruitType.Banana, SpanishFruitType.Platano}
,{FruitType.Mango, SpanishFruitType.Otra}
,{FruitType.Orange, SpanishFruitType.Naranja}
,{FruitType.Other, SpanishFruitType.Otra}
};
private static SpanishFruitType GetSpanishEquivalentWithDictionary(FruitType typeOfFruit)
{
return EnglishToSpanishFruit[typeOfFruit]; // throws exception if it's not in the dictionary, which is fine.
}
Run Code Online (Sandbox Code Playgroud)
字典不仅具有速度提升,而且代码中的不必要的字符串也更少.那么使用字典总是更好吗?还有第三种更好的方法吗?
提前致谢.
实际上,字典速度较慢.真.只写简单的基准测试(我添加了将字典转换为数组的示例):
void Main()
{
for (int itFac = 0; itFac < 7; itFac++ ) {
var iterations = 100;
iterations *= (int)Math.Pow(10, itFac);
Console.WriteLine("Iterations: {0}", iterations);
{
Random r = new Random();
int maxFruits = 5;
var timer = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++) {
var res = Fruits.GetSpanishEquivalentWithArray((Fruits.FruitType)r.Next(maxFruits));
}
Console.WriteLine("Array time: {0}", timer.Elapsed);
}
{
Random r = new Random();
int maxFruits = 5;
var timer = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++) {
var res = Fruits.GetSpanishEquivalent((Fruits.FruitType)r.Next(maxFruits));
}
Console.WriteLine("Switch time : {0}", timer.Elapsed);
}
{
Random r = new Random();
int maxFruits = 5;
var timer = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++) {
var res = Fruits.GetSpanishEquivalentWithDictionary((Fruits.FruitType)r.Next(maxFruits));
}
Console.WriteLine("Dictionary time: {0}", timer.Elapsed);
}
Console.WriteLine();
}
}
class Fruits {
public enum FruitType
{
Other,
Apple,
Banana,
Mango,
Orange
}
public enum SpanishFruitType
{
Otra,
Manzana, // Apple
Naranja, // Orange
Platano, // Banana
// let's say they don't have mangos, because I don't remember the word for it.
}
public static SpanishFruitType GetSpanishEquivalent(FruitType typeOfFruit)
{
switch(typeOfFruit)
{
case FruitType.Apple:
return SpanishFruitType.Manzana;
case FruitType.Banana:
return SpanishFruitType.Platano;
case FruitType.Orange:
return SpanishFruitType.Naranja;
case FruitType.Mango:
case FruitType.Other:
return SpanishFruitType.Otra;
default:
throw new Exception("what kind of fruit is " + typeOfFruit + "?!");
}
}
public static SpanishFruitType GetSpanishEquivalent(string typeOfFruit)
{
switch(typeOfFruit)
{
case "apple":
return SpanishFruitType.Manzana;
case "banana":
return SpanishFruitType.Platano;
case "orange":
return SpanishFruitType.Naranja;
case "mango":
case "other":
return SpanishFruitType.Otra;
default:
throw new Exception("what kind of fruit is " + typeOfFruit + "?!");
}
}
public static Dictionary<FruitType, SpanishFruitType> EnglishToSpanishFruit = new Dictionary<FruitType, SpanishFruitType>()
{
{FruitType.Apple, SpanishFruitType.Manzana}
,{FruitType.Banana, SpanishFruitType.Platano}
,{FruitType.Mango, SpanishFruitType.Otra}
,{FruitType.Orange, SpanishFruitType.Naranja}
,{FruitType.Other, SpanishFruitType.Otra}
};
public static SpanishFruitType GetSpanishEquivalentWithDictionary(FruitType typeOfFruit)
{
return EnglishToSpanishFruit[typeOfFruit]; // throws exception if it's not in the dictionary, which is fine.
}
public static SpanishFruitType[] EnglishToSpanishFruitArray;
static Fruits() {
EnglishToSpanishFruitArray = new SpanishFruitType[EnglishToSpanishFruit.Select(p => (int)p.Key).Max() + 1];
foreach (var pair in EnglishToSpanishFruit)
EnglishToSpanishFruitArray[(int)pair.Key] = pair.Value;
}
public static SpanishFruitType GetSpanishEquivalentWithArray(FruitType typeOfFruit)
{
return EnglishToSpanishFruitArray[(int)typeOfFruit]; // throws exception if it's not in the dictionary, which is fine.
}
}
Run Code Online (Sandbox Code Playgroud)
结果:
Iterations: 100
Array time : 00:00:00.0108628
Switch time : 00:00:00.0002204
Dictionary time: 00:00:00.0008475
Iterations: 1000
Array time : 00:00:00.0000410
Switch time : 00:00:00.0000472
Dictionary time: 00:00:00.0004556
Iterations: 10000
Array time : 00:00:00.0006095
Switch time : 00:00:00.0011230
Dictionary time: 00:00:00.0074769
Iterations: 100000
Array time : 00:00:00.0043019
Switch time : 00:00:00.0047117
Dictionary time: 00:00:00.0611122
Iterations: 1000000
Array time : 00:00:00.0468998
Switch time : 00:00:00.0520848
Dictionary time: 00:00:00.5861588
Iterations: 10000000
Array time : 00:00:00.4268453
Switch time : 00:00:00.5002004
Dictionary time: 00:00:07.5352484
Iterations: 100000000
Array time : 00:00:04.1720282
Switch time : 00:00:04.9347176
Dictionary time: 00:00:56.0107932
Run Code Online (Sandbox Code Playgroud)
怎么了.让我们看看生成的IL代码:
Fruits.GetSpanishEquivalent:
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.1
IL_0003: ldloc.1
IL_0004: switch (IL_002B, IL_001F, IL_0023, IL_002B, IL_0027)
IL_001D: br.s IL_002F
IL_001F: ldc.i4.1
IL_0020: stloc.0
IL_0021: br.s IL_004A
IL_0023: ldc.i4.3
IL_0024: stloc.0
IL_0025: br.s IL_004A
IL_0027: ldc.i4.2
IL_0028: stloc.0
IL_0029: br.s IL_004A
IL_002B: ldc.i4.0
IL_002C: stloc.0
IL_002D: br.s IL_004A
IL_002F: ldstr "what kind of fruit is "
IL_0034: ldarg.0
IL_0035: box UserQuery+Fruits.FruitType
IL_003A: ldstr "?!"
IL_003F: call System.String.Concat
IL_0044: newobj System.Exception..ctor
IL_0049: throw
IL_004A: ldloc.0
IL_004B: ret
Run Code Online (Sandbox Code Playgroud)
怎么了?切换发生了.对于有序的数值,可以优化切换,并通过从数组跳转到指针来替换.为什么真正的数组比switch - dunno工作得更快,它的工作速度更快.
好吧,如果你不使用枚举,但是使用字符串,在少数变体上切换和字典之间没有真正的区别.随着越来越多的变体字典变得更快.
选择什么?选择更适合您和您的团队的内容.当您看到您的解决方案产生性能问题时,您应该将Dictionary(如果使用它)替换为像我这样的切换或数组.如果很少调用您的翻译功能,则无需对其进行优化.
谈论你的案例 - 要获得翻译,所有解决方案都很糟糕.翻译必须存储在资源中.必须只有一个FruitType,没有其他枚举.