2013-04-04 14 views
9

Ho una struttura di dati costante che rappresenta l'altezza relativa di ogni vertebra umana, normalizzata in relazione all'altezza totale della colonna vertebrale. Questo deriva da studi antropometrici, eccDevo usare Enum, static Class, Dictionary o Struct per rappresentare questi "float etichettati" in C#?

ho implementato in Python come una tupla di tuple, ciascuna contenente una (stringa) Nome e (doppio) Valore, in questo modo:

vertebral_heights = (
("C7", 0.0000000), 
("T1", 0.0391914), 
("T2", 0.0785479), 
("T3", 0.1183993), 
("T4", 0.1590759), 
("T5", 0.2009076), 
("T6", 0.2442244), 
("T7", 0.2893564), 
("T8", 0.3366337), 
("T9", 0.3863861), 
("T10", 0.4389439), 
("T11", 0.4946370), 
("T12", 0.5537954), 
("L1", 0.6167492), 
("L2", 0.6838284), 
("L3", 0.7553630), 
("L4", 0.8316832), 
("L5", 0.9131188), 
("S1", 1.0000000)) 

mio il primo pensiero è stato quello di creare un dizionario, ma che avrebbe avuto bisogno di una classe come contenitore. Poi mi è venuta in mente l'idea di un Enum, ma ho letto "le enumerazioni sono per inti", e ho il doppio. Poi ci sono Class and Struct, ma a questo punto sono completamente confuso, e credo che la mia attuale comprensione delle migliori pratiche di fare questa roba in C# non sia ancora abbastanza.

La mia destinazione d'uso è avere una "mappa" tra il modello dell'applicazione (la parte numerica degli elementi) e il modello utente (la parte relativa agli elementi denominata, relativa al dominio).

Qualche suggerimento?

+3

Se è necessario accedervi in ​​modo dinamico (tramite il nome dell'etichetta), un dizionario è probabilmente il migliore. Se si stanno facendo riferimento solo a nome di variabile, allora un gruppo di costanti in una singola classe probabilmente sta bene (dato che non ce ne sono davvero tanti) – musefan

+0

@musefan 1) sarebbero statici/costanti/readonly; 2) Vorrei conservare un "ordine relativo" tra di loro, in un senso "IEnumerable"; Ottenere i valori tramite 'vertebral_heights.L3', ad esempio, sarebbe bello. – heltonbiker

+0

Per una raccolta così piccola, Elenco <> potrebbe essere più veloce di un dizionario. Benchmark, se le prestazioni sono importanti .. – nawfal

risposta

5

in realtà dipende da come vuoi accedere ai valori.

Costanti

Se si intende utilizzare sempre i nomi delle variabili, ad esempio:

double x = C7; 

allora si può solo utilizzare una classe piena di costanti in questo modo:

public class VertebralHeights 
{ 
    public const double C7 = 0.0000000d; 
} 

Dizionario

Tuttavia, se si desidera accedere in modo dinamico, ad esempio:

string id = "C7"; 
double x = VertebralHeights[id]; 

allora si sarà meglio con un Dictionary quale è possibile definire in questo modo:

Dictionary<string, double> VertebralHeights = new Dictionary<string, double>() 
{ 
    { "C7", 0.0000000d }, 
    { "T1", 0.0391914d} 
} 

Avere entrambi i modi insieme.

Se si desidera sia l'accesso dinamico fortemente digitato al valore che è possibile estendere uno dei metodi sopra ...

Per costanti (metodo 1) aggiungere una funzione che prende una stringa:

public double GetValue(string s) 
{ 
    switch(s) 
    { 
     case "C7": return C7; 
     case "T7": return T7; 
     //...and so on... 
     default: return 0;//or an alternate default 
    } 
} 

(nota: si potrebbe fare questo con la riflessione, invece, che sarebbe più facile con un elenco enorme, ma isn' t davvero la pena la performance migliore colpito qui)

Per la Dizionario approccio (metodo 2), si potrebbe aggiungere una collezione di getter:

public double C7 { get { return VertebralHeights["C7"]; } } 
+0

La tua pila di istruzioni if ​​farebbe bene come una dichiarazione di switch –

+1

@DeniseSkidmore: Sì, ho aggiunto una nota al riguardo. Ho appena fatto questo esempio perché non potevo essere disturbato a scrivere un interruttore per essere onesto. avvitarlo, lo cambierò – musefan

2

Dipende da come si utilizzano tali mapping. Se sono necessarie ricerche per nome (string), allora Dictionary è la scelta giusta. Ma se hai bisogno solo di quei numeri per avere nomi amichevoli, andrei per costanti in una classe (forse statico).

E 'anche facile enumerare entrambe le chiavi e valori in un dizionario:

var dict = new Dictionary<string, double>(); 

foreach (var key in dict.Keys) 
{ 

} 

foreach (var value in dict.Values) 
{ 

} 
+0

Posso enumerarli in ordine? Qualcosa come "foreach value in values" o "foreach label in labels"? – heltonbiker

+0

Tale enumerazione non sarebbe nell'ordine corretto. – plinth

+0

Assolutamente, tuttavia è necessario gestire l'ordine. Aggiunto codice di esempio alla mia risposta. –

3

Si potrebbe creare una classe:

public static class VertebralHeights 
{ 
    public const double C7 = 0.0000000; 
    public const double T1 = 0.0391914; 
    //... 
} 

Accesso: double c7 = VertebralHeights.C7;

+0

E probabilmente si vorrà anche il doppio statico pubblico FromName (nome stringa) {/ * ... * /} – plinth

+0

+1 per il opzione più semplice e più performante :) – nawfal

5

Ecco il mio prendere su questo - lavoro con una classe Singleton che è un dizionario:

public class Vertebrae : Dictionary<string, double> 
{ 
    private Vertebrae() : base() { } 


    private static Vertebrae _heights = new Vertebrae() { 
     { "C7", 0.0 }, 
     { "T1", 0.0391914 }, 
     { "T2", 0.0785479 }, 
    }; 

    public static Vertebrae Heights { get { return _heights; } } 

    public static double C7 { get { return Heights["C7"]; } } 
    public static double T1 { get { return Heights["T1"]; } } 
    public static double T2 { get { return Heights["T2"]; } } 

    public static IEnumerable<double> All 
    { 
     get 
     { 
      return new List<double>() { C7, T1, T2 }; 
     } 
    } 
} 

Per accedere le vertebre per nome della stringa, si fa:

double c7 = Vertebrae.Heights["C7"]; 

Per accedere le vertebre dalla simbolica nome, si fa:

double c7 = Vertebrae.C7; 

Per enumerare le vertebre si fa:

foreach (double v in Vertebrae.All) { /* ... */ } 

Per l'enumeratore si potrebbe avere un unico elenco statico inizializzato come nel enumeratore, ma non ero sicuro che sarebbe ottenere inizializzato prima, l'elenco statico o il dizionario statica ...

+0

Vedo che stai provando a fare della classe un dizionario statico, ma questo non rende 'Vertebrae.Heights.Heights' Bella? Potrebbe essere meglio incapsulare il dizionario invece di ereditarlo. Hai già dovuto scrivere funzioni di incapsulamento per _tezza dal momento che non stai utilizzando la memoria del dizionario nativo ovunque. Peccato che una classe statica non possa implementare un'interfaccia però ... –

+2

Penso che stavo andando fino a 'public double double C7 {get {return Heights [" C7 "]; }} 'Creo solo C7 una stringa costante' public static string C7 = "C7" 'e l'utente potrebbe usare' Vertebrae.Heights [Vertebrae.C7] ' –

+1

No - puoi solo fare riferimento a un membro statico di una classe non da un'istanza della classe, quindi "Vertebrae.Heights.Heights" è illegale. – plinth

2

enumerarli , trovali per stringa e ordinali per ordine, è necessario memorizzare tre pezzi di dati. Il modo in cui accedi a loro cambia il modo migliore per memorizzarli.

  1. List di tuple contenenti nome e valore.
    • Questo ha il vantaggio principale di ordinare.
    • Per ottenere un oggetto specifico per nome, è necessario una query linq per recuperarlo.
  2. Dictionary con chiave di nome, e il valore di tuple ordine e valore
    • Questo ha il vantaggio principale di trovare oggetti rapidamente per nome
    • Al fine di ottenere tutti gli elementi in un ordine specifico contenente voi Avrei bisogno di una query di linq per ordinarli.
    • Se si desidera eseguire il loop su tutti gli elementi senza un ordine specifico, Dictionary lo consentirà.
  3. Mantiene un dizionario e un elenco di valori. - Un po 'disordinato, potrebbe essere un eccesso di ottimizzazione.
  4. Creare una raccolta personalizzata che erediti da una delle implementazioni precedenti e ha fornito la funzionalità mancante che nasconde l'implementazione effettiva. Linq non è così lento da essere preoccupante con una breve lista.
+0

Mi è piaciuto il tuo approccio N ° 3. Ho iniziato a scrivere una classe con due campi privati: una serie di doppi e un enum di nomi. Ma dal momento che non sono sicuro di come utilizzerei questo codice, ho rinviato l'implementazione di getter ed enumeratori quando uso effettivamente il codice. (Sono così incerto che quasi non mi sento nemmeno in grado di accettare alcuna risposta ...: o ( – heltonbiker

1

È possibile utilizzare una classe semplice in una raccolta generica. Quindi LINQ semplifica la mappatura dei valori tra loro.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace ConsoleApplication1 
{ 

    class Vertebra { 
     public string name { get; set; } 
     public double height { get; set; } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      List<Vertebra> Vertebrae = new List<Vertebra>() { 
       new Vertebra() {name = "C7", height = 0.0000000}, 
       new Vertebra() {name = "T1", height = 0.0391914} 
       //etc 
      }; 

      //find height by name: 
      double H = Vertebrae.Single(v => v.name == "C7").height; 

      //find name by height: 
      string N = Vertebrae.Single(v => v.height == 0.0391914).name; 

     } 
    } 

} 
+0

+1, per una piccola raccolta, la lista darebbe prestazioni migliori! Rendila una collezione personalizzata per evitare il fastidio di chiamare – nawfal

4

Fatelo come enume e scrivete il codice idraulico della scatola nera in avanti. Non ve ne pentirete! Ecco quello che vorrei fare:

Scrivi un attributo personalizzato in modo che è possibile associare il doppio valore ad ogni enum:

[AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)] 
internal sealed class VertebralHeightAsDoubleAttribute : Attribute 
{ 
    public double HeightValue { get; private set; } 

    public VertebralHeightAsDoubleAttribute(double heightValue_) 
    { 
    HeightValue = heightValue_; 
    } 
} 

Alcuni metodi di estensione per rendere la vita più facile:

public static class VHAttribExtensions 
{ 
    public static string ToNameString(this VertebralHeight target) 
    { 
    return Enum.GetName(typeof(VertebralHeight), target); 
    } 

    public static double ToHeightValue(this VertebralHeight target) 
    { 
    var fi = target.GetType().GetField(target.ToString()); 
    var attributes = (VertebralHeightAsDoubleAttribute[])fi.GetCustomAttributes(
     typeof(VertebralHeightAsDoubleAttribute), false); 
    return attributes.Length > 0 ? attributes[0].HeightValue : double.NaN; 
    } 
} 

Definisci la tua enum utilizzando l'attributo personalizzato:

public enum VertebralHeight 
{ 
    [VertebralHeightAsDouble(0.0000000)] 
    C7, 
    [VertebralHeightAsDouble(0.0391914)] 
    T1, 
    [VertebralHeightAsDouble(0.0785479)] 
    T2, 
    [VertebralHeightAsDouble(0.1183993)] 
    T3, 
    [VertebralHeightAsDouble(0.1590759)] 
    T4, 
    [VertebralHeightAsDouble(0.2009076)] 
    T5, 
    [VertebralHeightAsDouble(0.2442244)] 
    T6, 
    [VertebralHeightAsDouble(0.2893564)] 
    T7, 
    [VertebralHeightAsDouble(0.3366337)] 
    T8, 
    [VertebralHeightAsDouble(0.3863861)] 
    T9, 
    [VertebralHeightAsDouble(0.4389439)] 
    T10, 
    [VertebralHeightAsDouble(0.4946370)] 
    T11, 
    [VertebralHeightAsDouble(0.5537954)] 
    T12, 
    [VertebralHeightAsDouble(0.6167492)] 
    L1, 
    [VertebralHeightAsDouble(0.6838284)] 
    L2, 
    [VertebralHeightAsDouble(0.7553630)] 
    L3, 
    [VertebralHeightAsDouble(0.8316832)] 
    L4, 
    [VertebralHeightAsDouble(0.9131188)] 
    L5, 
    [VertebralHeightAsDouble(1.0000000)] 
    S1 
} 

prova esso:

static void Main(string[] args) 
{ 
    var list = Enum.GetValues(typeof(VertebralHeight)).OfType<VertebralHeight>(); 
    foreach (var vh in list) 
    { 
    Console.WriteLine("{0} : {1}", vh.ToNameString(), vh.ToHeightValue()); 
    } 
    Console.ReadLine(); 
} 
+0

Questo è molto interessante e anche molto "avanzato" Anche se, quando guardo alla mia ingenua implementazione Python, "sente" che meritava (e molto probabilmente avrebbe potuto) un'implementazione più snella ... Alcuni tipi di la verbosità C# non mi sembra ancora giusta per me ...: oP – heltonbiker

+4

Sì, questo approccio praticamente elimina la necessità di scrivere qualsiasi speciale istruzione 'switch'. Inoltre elimina un sacco di duplicazione del codice.Aggiungere nuovi membri di enum è abbastanza facile (anche se dubito che il corpo umano possa sviluppare nuove ossa vertebrali nel prossimo futuro, lol). – code4life

+0

@heltonbiker questo è un approccio abbastanza pulito, cosa intendi per verbosità? Puoi dare un nome all'attributo come preferisci, anche solo "H" se è quello a cui ti trovi, lo stesso vale per 'ToNameString()' ('Name()'?) E 'ToHeightValue()'. Quindi avrai un approccio fortemente tipizzato, senza stringhe. Per quanto riguarda i due blocchi di codice per definire l'argomento e due metodi di supporto: li mettete in file separati che non dovrete mai più rivedere. E sono anche facilmente testabili. – CodeCaster