枚举案例处理 - 更好地使用开关或字典? [英] enum case handling - better to use a switch or a dictionary?

查看:146
本文介绍了枚举案例处理 - 更好地使用开关或字典?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在逐个处理枚举的值时,最好使用switch语句或字典吗?

When handling the values of an enum on a case by case basis, is it better to use a switch statement or a dictionary?

我会认为字典会更快在空间方面,它占用了一些内存,但是case语句也将占用程序本身所需的内存中的一些内存。所以底线我认为只是使用字典总是更好。

I would think that the dictionary would be faster. In terms of space, it takes up some in memory, but the case statement would also take up some memory just in the memory needed for the program itself. So bottom line I'm thinking it's always better to just use the dictionary.

这两个实现并排进行比较:

Here are the two implementations side by side for comparison:

给定这些枚举:

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.
}

以下是使用switch语句执行此操作的方法:

Here is the way to do it with the switch statement:

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 + "?!");
    }
}

这里是如何用字典完成的:

And here is how it is done with a dictionary:

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.
}

字典不仅具有提升速度,还具有较少的不必要的字符串在代码中。这样总是更好地使用字典吗?有没有第三个更好的方法?

Not only does the dictionary have the speed boost, it also has less unnecessary strings in the code. Is this always better to use the dictionary then? Is there even a third better way?

提前感谢。

推荐答案

其实字典比较慢。真。只是写简单的基准(我添加了将字典转换为数组)的示例:

In fact, dictionary is slower. Really. Just write simple benchmark (I added example with converting dictionary to array):

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.
    }
}

结果:

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

会发生什么。我们来看看生成的IL代码:

What happens. Let's look on the generated IL code:

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         

会发生什么? Switch 发生。对于顺序数量的值,可以通过跳转到数组中的指针来优化切换。为什么真正的数组的工作速度比switch-dunno更快,只是工作得更快。

What happens? Switch happens. For sequenced number of values switch can be optimized and replaced by jump to pointer from array. Why real array works faster than switch - dunno, just works faster.

嗯,如果你没有使用枚举,但是使用字符串,切换和字典之间没有真正的区别少数变体。随着越来越多的变体词典更快。

Well, if you works not with enums, but with string there are no real difference between switch and dictionary on small number of variants. With more and more variants dictionary is faster.

选择什么?选择为您和您的团队更容易阅读的内容。当你看到这一刻创建性能问题时,你必须替换Dictionary(如果你使用它)来转换或者像I这样的数组。当你的翻译函数很少需要优化的时候。

What to choose? Choose what is easier to read for you and your team. When you see that this moment creates perfomance issues you must replace Dictionary (if you use it) to switch or array like I. When your function of translation calls rarely there are no need to optimize it.

谈论你的情况 - 要翻译,所有的解决方案都是坏的。翻译必须储存在资源中。必须只有一个FruitType,没有其他枚举。

Talking about your case - to get translation, all solutions are bad. Translations must be stored in resources. There must be only one FruitType, no other enums.

这篇关于枚举案例处理 - 更好地使用开关或字典?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆