2010-07-15 9 views
22

mia app ha un sacco di diversi valori di ricerca, questi valori non cambiare mai, ad esempio, Stati Uniti. Piuttosto che inserirli nelle tabelle del database, mi piacerebbe usare le enumerazioni.Enum e prestazioni

Ma, mi rendo conto che farlo in questo modo comporta avere alcune enumerazioni e un sacco di casting da "int" e "string" da e verso le mie enumerazioni.

Alternativa, vedo qualcuno menzionato usando un dizionario <> come tabelle di ricerca, ma l'implementazione di enum sembra essere più pulita.

Quindi, mi piacerebbe chiedere se mantenere e passare un sacco di enumerazioni e lanciarle rappresenta un problema per le prestazioni o dovrei usare l'approccio delle tabelle di ricerca, che offre prestazioni migliori?

Edit: Il casting è necessario come ID per essere memorizzati in altre tabelle di database.

+3

Perché avrete bisogno di gettare loro? – LukeH

+2

@LukeH: Presumibilmente perché nel database i valori saranno solo numeri interi. –

+0

@Jon: Questo è quello che ho indovinato, ma sarebbe bello ottenere dei chiarimenti dall'OP. – LukeH

risposta

29

Casting da int ad un enum è estremamente a buon mercato ... che sarà più veloce di una ricerca dizionario. Fondamentalmente è un no-op, basta copiare i bit in una posizione con un diverso tipo di noion.

L'analisi di una stringa in un valore enum sarà leggermente più lenta.

Dubito che questo sarà un collo di bottiglia per te comunque lo fai però, ad essere onesti ... senza sapere di più su quello che stai facendo, è un po 'difficile dare consigli oltre il normale "scrivi il più semplice , codice leggibile e gestibile in modalità funzionante, quindi controlla che funzioni abbastanza bene. "

+0

Questa potrebbe essere un'altra domanda, ma relativa: è il cast delle enumerazioni l'una con l'altra con corrispondenti valori di 'int 'sottostanti tanto convenienti quanto il cast' (int) '? –

+1

@CADBloke: Mi aspetterei di sì, sì. Ma prova se è importante per te :) –

13

Non noterai una grande differenza di prestazioni tra i due, ma ti consiglio comunque di utilizzare un dizionario perché ti darà un po 'più di flessibilità in futuro.

Per prima cosa, un Enum in C# non può avere automaticamente una classe associata ad esso come in Java, quindi se si desidera associare informazioni aggiuntive con uno stato (Nome completo, Capitale, Abbreviazione postale, ecc.) , creando una classe UnitedState sarà più semplice raggruppare tutte queste informazioni in una raccolta.

Inoltre, anche se si si pensa che questo valore non cambierà mai, non è perfettamente immutabile. Ad esempio, potresti avere un nuovo requisito per includere i territori. O forse dovrai consentire agli utenti canadesi di vedere i nomi delle province canadesi. Se si considera questa raccolta come qualsiasi altra raccolta di dati (utilizzando un repository per recuperare i valori da essa), in seguito si avrà la possibilità di modificare l'implementazione del repository per estrarre valori da un'origine diversa (database, servizio Web, sessione, ecc.). Le enumerazioni sono molto meno versatili.

Modifica

Per quanto riguarda l'argomento delle prestazioni: Tenete a mente che non sei solo esprimono un Enum per un int: si sta anche eseguendo ToString() su tale enum, che aggiunge un notevole tempo di elaborazione. Si consideri il seguente test:

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); 

Risultati:

Enum: 26.4875 
Dictionary: 0.7684 

Quindi, se le prestazioni sono davvero la vostra preoccupazione principale, un dizionario è sicuramente la strada da percorrere. Tuttavia, stiamo parlando di tempi così veloci che ci sono una mezza dozzina di altre preoccupazioni che vorrei affrontare prima che mi interessasse anche il problema della velocità.

enumerazioni in C# non sono stati progettati per fornire mappature tra i valori e stringhe. Sono stati progettati per fornire valori costanti fortemente tipizzati che è possibile aggirare nel codice. I due vantaggi principali di questo sono:

  1. avete un indizio in più del compilatore-controllato per aiutarvi ad evitare il passaggio di argomenti nell'ordine sbagliato, ecc
  2. piuttosto che mettere i valori numerici "magici" (ad esempio "42 ") nel tuo codice, puoi pronunciare" States.Oklahoma ", che rende il tuo codice più leggibile.

A differenza di Java, C# non controlla automaticamente i valori del cast per garantire che essi siano validi (myState = (States)321), in modo da non si ottiene alcun controllo dati runtime sugli ingressi senza fare manualmente. Se non si dispone del codice che si riferisce esplicitamente agli stati ("States.Oklahoma"), allora non si ottiene alcun valore dalla # 2 sopra. Questo ci lascia con # 1 come l'unica vera ragione per usare le enumerazioni. Se questo è un motivo sufficiente per te, suggerirei di utilizzare le enumerazioni invece degli interi come valori chiave. Quindi, quando hai bisogno di una stringa o di qualche altro valore relativo allo stato, esegui una ricerca del dizionario.

Ecco come lo farei:

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); 
    } 
} 

questo modo:

  1. si arriva a passare i valori intorno fortemente tipizzato che rappresentano uno Stato,
  2. tua ricerca ottiene fail comportamento veloce se viene fornito un input non valido,
  3. è possibile modificare facilmente dove risiedono i dati dello stato effettivo in futuro,
  4. è possibile aggiungere facilmente dati relativi allo stato alla classe State in futuro,
  5. è possibile aggiungere facilmente nuovi stati, territori, distretti, province o qualsiasi altra cosa in futuro.
  6. ottenere un nome da un int è ancora circa 15 volte più veloce rispetto a quando si utilizza Enum.ToString().

[grunt]

+1

+1, sono completamente d'accordo con la parte "non perfettamente immutabile". Ti odierà se la tua applicazione di dieci anni si basa su un modello politico del passato e non hai più supporto per questo ... –

+1

Non sei d'accordo sul fatto che non ci sia una grande differenza di prestazioni; le enumerazioni in C# sono solo intere, quindi è molto più veloce gettare tra loro e int. I tuoi punti sono buoni, ma sarei costretto a raccomandare di sacrificare le prestazioni per una flessibilità forse inutile. – TMN

+2

@TMN: Anche se le enumerazioni fossero più veloci, stiamo parlando di velocità così elevate che non sarebbe evidente nella maggior parte delle applicazioni. Sarebbe difficile raccomandare di sacrificare la flessibilità per un minimo miglioramento delle prestazioni. Tuttavia, come si scopre, una ricerca di dizionario sarà in realtà molto più veloce per il tipo di cose di cui sta parlando. Vedi la mia risposta aggiornata. – StriplingWarrior

0

Le enumerazioni superano di gran lunga qualsiasi cosa, specialmente quelle del dizionario. Enum utilizza solo un singolo byte. Ma perché vorresti essere scelto? Sembra che dovresti usare le enumerazioni ovunque.

+5

Le enumerie solitamente usano 4 byte - il loro tipo di base predefinito è 'int' a meno che tu non dichiari esplicitamente il contrario. – LukeH

+1

La parte "molto più performante" dipende in gran parte da ciò che si sta facendo con l'enumerazione una volta ottenuta. Se lo usi in un'istruzione switch, le enumerazioni saranno più veloci. Se chiami ToString su di esso, un dizionario pre-compilato sarà più veloce. Vedi la mia risposta. – StriplingWarrior

-2

Evita enum come puoi: le enumerazioni devono essere sostituite da singleton derivanti dalla classe base o dall'implementazione di un'interfaccia.

La pratica di usare enum deriva da un vecchio stile di programmazione di C.

si inizia ad utilizzare un enum per gli Stati Uniti d'America, allora si avrà bisogno il numero di abitanti, la capitale ..., e si avremo bisogno di molti grandi switch per ottenere tutte queste informazioni.

+0

Enum in C# non sono oggetti. In realtà non derivano da una classe base o implementano un'interfaccia. Sono solo costanti del tipo sicuro. Possono essere utili se usati come tali, ma sono pericolosi se fraintesi. – StriplingWarrior

+0

So che le enumerazioni non sono oggetto. Questa è la ragione per cui dico che sono inutili. Comunque ho chiarito la mia risposta. – onof

2

Si potrebbe utilizzare TypeSafeEnum s classe

Ecco una base

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 

Ed ecco un 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 

e diventa più facile aggiungere Internazionalizzazione.

Mi dispiace per il fatto che sia in VB e in francese.

Cheers!

+0

Poiché i valori interi sono mappati nel database, sarebbe opportuno rendere possibile specificare valori int espressi, in modo che non cambino se vengono riordinati. – StriplingWarrior

+0

Beh, penso che non sarebbe troppo difficile cambiare il codice per consentirlo. –

0

In alternativa è possibile utilizzare costanti

+0

In che modo utilizzare le costanti è vantaggioso qui? – StriplingWarrior