枚举和表现

时间:2010-07-15 14:46:36

标签: c# performance dictionary enums casting

我的应用有很多不同的查找值,这些值永远不会改变,例如美国各州。我不想把它们放到数据库表中,而是想使用枚举。

但是,我确实意识到这样做需要一些枚举和大量的“int”和“string”来自我的枚举。

另外,我看到有人提到使用字典<>作为查找表,但枚举实现似乎更清晰。

所以,我想问一下,保留和传递很多枚举并将它们转化为性能问题,还是应该使用查找表方法,哪种方式表现更好?

编辑:需要将转换作为ID存储在其他数据库表中。

7 个答案:

答案 0 :(得分:32)

int转换为枚举非常便宜......它比字典查找更快。基本上它是一个无操作,只是将位复制到具有不同名义类型的位置。

将字符串解析为枚举值会稍慢。

我怀疑这对你来说是一个瓶颈但是你这样做,说实话......不知道你在做什么,有点难以推荐超出正常范围“写出最简单的,模式可读和可维护的代码,它将起作用,然后检查它是否运行良好。“

答案 1 :(得分:15)

你不会注意到两者之间在性能方面存在很大差异,但我仍然建议使用词典,因为它将来会给你一点灵活性。

首先,C#中的Enum不能像Java一样自动拥有与之关联的类,因此如果要将其他信息与州(全名,首都城市,邮政缩写等)相关联,创建一个UnitedState类可以更容易地将所有信息打包到一个集合中。

此外,即使你认为这个值永远不会改变,它也不是完全不可变的。例如,你可以想象有一个新的要求包括领土。或者,您可能需要允许加拿大用户查看加拿大省的名称。如果您将此集合视为任何其他数据集合(使用存储库从中检索值),您稍后可以选择更改存储库实现以从其他来源(数据库,Web服务,会话等)提取值。 )。枚举的用途要少得多。

修改

关于性能参数:请记住,您不是只是将枚举转换为int:您还在该枚举上运行ToString(),这会增加相当多的处理时间。考虑以下测试:

const int C = 10000;
int[] ids = new int[C];
string[] names = new string[C];
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i< C; i++)
{
    var id = (i % 50) + 1;
    names[i] = ((States)id).ToString();
}
sw.Stop();
Console.WriteLine("Enum: " + sw.Elapsed.TotalMilliseconds);
var namesById = Enum.GetValues(typeof(States)).Cast<States>()
                .ToDictionary(s => (int) s, s => s.ToString());
sw.Restart();
for (int i = 0; i< C; i++)
{
    var id = (i % 50) + 1;
    names[i] = namesById[id];
}
sw.Stop();
Console.WriteLine("Dictionary: " + sw.Elapsed.TotalMilliseconds);

结果:

Enum: 26.4875
Dictionary: 0.7684

因此,如果性能确实是您最关心的问题,那么词典绝对是您的选择。但是,我们在这里谈论如此快速的时间,在我甚至不关心速度问题之前,还有其他六个问题需要解决。

C#中的枚举不是为了提供值和字符串之间的映射而设计的。它们旨在提供强类型常量值,您可以在代码中传递它们。这样做的两个主要优点是:

  1. 你有一个额外的编译器检查线索,以帮助你避免以错误的顺序传递参数等。
  2. 您可以说“States.Oklahoma”,而不是在您的代码中添加“神奇”数字值(例如“42”),这会使您的代码更具可读性。
  3. 与Java不同,C#不会自动检查强制转换值以确保它们有效(myState = (States)321),因此如果不手动执行,则不会对输入进行任何运行时数据检查。如果您没有明确引用状态的代码(“States.Oklahoma”),那么您不会从上面的#2获得任何值。这使得#1成为使用枚举的唯一真正原因。如果这是一个足够好的理由,那么我建议使用枚举而不是ints 作为键值。然后,当您需要字符串或与状态相关的其他值时,请执行字典查找。

    我是这样做的:

    public enum StateKey{
        AL = 1,AK,AS,AZ,AR,CA,CO,CT,DE,DC,FM,FL,GA,GU,
        HI,ID,IL,IN,IA,KS,KY,LA,ME,MH,MD,MA,MI,MN,MS,
        MO,MT,NE,NV,NH,NJ,NM,NY,NC,ND,MP,OH,OK,OR,PW,
        PA,PR,RI,SC,SD,TN,TX,UT,VT,VI,VA,WA,WV,WI,WY,
    }
    
    public class State
    {
        public StateKey Key {get;set;}
        public int IntKey {get {return (int)Key;}}
        public string PostalAbbreviation {get;set;}
    
    }
    
    public interface IStateRepository
    {
        State GetByKey(StateKey key);
    }
    
    public class StateRepository : IStateRepository
    {
        private static Dictionary<StateKey, State> _statesByKey;
        static StateRepository()
        {
            _statesByKey = Enum.GetValues(typeof(StateKey))
            .Cast<StateKey>()
            .ToDictionary(k => k, k => new State {Key = k, PostalAbbreviation = k.ToString()});
        }
        public State GetByKey(StateKey key)
        {
            return _statesByKey[key];
        }
    }
    
    public class Foo
    {
        IStateRepository _repository;
        // Dependency Injection makes this class unit-testable
        public Foo(IStateRepository repository) 
        {
            _repository = repository;
        }
        // If you haven't learned the wonders of DI, do this:
        public Foo()
        {
            _repository = new StateRepository();
        }
    
        public void DoSomethingWithAState(StateKey key)
        {
            Console.WriteLine(_repository.GetByKey(key).PostalAbbreviation);
        }
    }
    

    这样:

    1. 您可以传递代表州的强类型值,
    2. 如果给出无效输入,则查找会获得快速失败的行为,
    3. 您可以轻松更改将来实际状态数据所在的位置,
    4. 您可以在以后轻松地将状态相关数据添加到State类中,
    5. 您可以轻松添加新的州,地区,地区,省份或其他任何其他内容。
    6. 从使用Enum.ToString()获取int的名称​​的速度的速度提高了15倍
    7. [咕噜]

答案 2 :(得分:2)

您可以使用TypeSafeEnum s

这是一个基类

Public MustInherit Class AbstractTypeSafeEnum
    Private Shared ReadOnly syncroot As New Object
    Private Shared masterValue As Integer = 0

    Protected ReadOnly _name As String
    Protected ReadOnly _value As Integer

    Protected Sub New(ByVal name As String)
        Me._name = name
        SyncLock syncroot
            masterValue += 1
            Me._value = masterValue
        End SyncLock
    End Sub

    Public ReadOnly Property value() As Integer
        Get
            Return _value
        End Get
    End Property

    Public Overrides Function ToString() As String
        Return _name
    End Function

    Public Shared Operator =(ByVal ats1 As AbstractTypeSafeEnum, ByVal ats2 As AbstractTypeSafeEnum) As Boolean
        Return (ats1._value = ats2._value) And Type.Equals(ats1.GetType, ats2.GetType)
    End Operator

    Public Shared Operator <>(ByVal ats1 As AbstractTypeSafeEnum, ByVal ats2 As AbstractTypeSafeEnum) As Boolean
        Return Not (ats1 = ats2)
    End Operator

End Class

这是一个Enum:

Public NotInheritable Class EnumProcType
    Inherits AbstractTypeSafeEnum

    Public Shared ReadOnly CREATE As New EnumProcType("Création")
    Public Shared ReadOnly MODIF As New EnumProcType("Modification")
    Public Shared ReadOnly DELETE As New EnumProcType("Suppression")

    Private Sub New(ByVal name As String)
        MyBase.New(name)
    End Sub

End Class

添加国际化变得更容易。

很抱歉这是在VB和法语中的事实。

干杯!

答案 3 :(得分:0)

Enums几乎可以胜过任何东西,尤其是字典。枚举仅使用单个字节。但是你为什么要施展?好像你应该在任何地方使用枚举。

答案 4 :(得分:0)

或者你可以使用常量

答案 5 :(得分:0)

如果问题是“转换枚举比访问字典项更快?”那么解决性能各个方面的其他答案就有意义了。

但这里的问题似乎是“当我需要将枚举值存储到数据库表时是否正在转换枚举会对应用程序性能产生负面影响?”。

如果是这种情况,我不需要运行任何测试来说明在数据库表中存储数据总是比转换枚举或执行其 ToString() 慢几个数量级。

在这种情况下,我会说重要的是代码的可读性和可维护性。在简单的情况下,枚举会干净利落地完成这项工作,但我同意其他答案,即从长远来看,字典会更加灵活。

答案 6 :(得分:-2)

尽可能避免枚举:枚举应该替换从基类派生的单例或实现接口。

使用枚举的做法来自于C的旧式编程。

你开始为美国使用enum,然后你将需要居民的数量,国会大厦......,你将需要很多大开关来获得所有这些信息。