2010-02-13 22 views
23

Ci sono alcune domande correlate here e here, ma in realtà non mi hanno dato risposte soddisfacenti. Il problema è che le enumerazioni nidificate in una classe in C# non possono avere lo stesso nome di una proprietà della classe. Il mio esempio:Nested enum e conflitti di denominazione delle proprietà

public class Card 
{ 
    public enum Suit 
    { 
     Clubs, 
     Diamonds, 
     Spades, 
     Hearts 
    } 

    public enum Rank 
    { 
     Two, 
     Three, 
     ... 
     King, 
     Ace 
    } 

    public Suit Suit { get; private set; } 
    public Rank Rank { get; private set; } 
    ... 
} 

Ci sono alcune opzioni per hackerare intorno a questo, ma non mi sembra giusto.

Potrei spostare l'enum fuori dalla classe, ma poi diresti semplicemente Suit invece di Card.Suit, che mi sembra sbagliato. Che cos'è un Suit al di fuori del contesto di un Card?

li riuscivo a muovere al di fuori della classe e cambiarli in qualcosa di simile CardSuit e CardRank, ma poi mi sento come se fossi la cottura le informazioni di contesto nel nome della enum, quando questo dovrebbe essere gestita da una classe o nome dello spazio dei nomi.

potevo cambiare i nomi delle enumerazioni per Suits e Ranks, ma questo viola Microsoft's naming guidelines. E non sembra giusto.

Potrei cambiare i nomi delle proprietà. Ma a cosa? Mi sembra intuitivamente giusto dire che voglio dire .

È possibile spostare l'enumerazione in una classe statica separata denominata CardInfo contenente solo queste enumerazioni. Se non riesco a trovare qualcos'altro, penso che questa sia l'opzione migliore.

Quindi mi chiedo cosa hanno fatto le altre persone in situazioni simili. Sarebbe anche bello sapere perché questo non è consentito. Forse Eric Lippert o qualcuno potrebbe prendere in considerazione la decisione di vietarlo? Sembra che crei solo l'ambiguità all'interno della classe, e questo potrebbe essere risolto forzando l'uso di this.Suit per il nome della proprietà. (Simile al disambiguazione tra locali e membri.) Presumo che questo sia stato omesso a causa della cosa "every feature starts with -100 points", ma sarei curioso di discutere su questo argomento.

+1

Grazie per il feedback, tutti. Vado con 'CardInfo', ma non penso che ci sia una risposta canonica qui. Ci sono molte opzioni decenti e penso che dipenda dallo stile. –

+0

Annidare 'enum' è ok se li userai solo (soprattutto?) All'interno della classe. Quindi devi nominare enum' * leggermente * in modo diverso (aggiungendo * Tipo *, * Modalità *, * Opzione *, ecc. Al nome) per evitare conflitti di nome con campo/proprietà. Se ci saranno molti utenti di quel 'enum' al di fuori della classe, allora l'annidamento è una cattiva idea. Puoi dichiararlo nello stesso file CS (per avere almeno una sorta di relazione con la classe in cui è usato). Anche la nidificazione è indesiderabile in wpf (non necessariamente 'enum', ma in generale). – Sinatr

risposta

18

sarebbe anche bello sapere perché questo non è consentita. Forse Eric Lippert o qualcuno potrebbe prendere in considerazione la decisione di vietarlo?

Il punto della regola è garantire che non ci sia ambiguità all'interno della classe quando si cerca un nome. Alcune regioni di codice sono designate come definenti uno 'spazio di dichiarazione'. La regola fondamentale di spazi dichiarazione è non ci sono due cose dichiarati nello stesso spazio di dichiarazione hanno lo stesso nome (tranne che per i metodi, che deve differire di firma, non nome.)

fare eccezioni a questa regola rende le cose più confuse, non meno confuse. Sono d'accordo che è irritante che tu non possa avere una proprietà e una enum dello stesso nome dichiarata nello stesso spazio di dichiarazione, ma una volta che inizi a fare eccezioni, allora diventa un casino. Di solito è una proprietà piacevole che un nome in modo univoco identifica un gruppo di metodi, un parametro di tipo, una proprietà e così via.

Si noti che questa regola si applica alle cose dichiarato in uno spazio di dichiarazione, non le cose utilizzati in uno spazio di dichiarazione. È perfettamente legale dire "Suit Suit pubblico {get; set;}" purché il tipo Suit non sia dichiarato nello stesso spazio dichiarativo della proprietà. Quando qualcuno dice "Suit.X", capire se X è sul tipo (cioè X è un membro statico) o la proprietà (cioè X è un membro di istanza) è un po 'complicato. Vedere il mio articolo su come lo facciamo per i dettagli:

http://blogs.msdn.com/ericlippert/archive/2009/07/06/color-color.aspx

+0

Questo ha senso per me. Grazie per le informazioni! –

+0

Esiste una sorta di convenzione di denominazione standard per affrontare questo problema? Nel mio caso particolare, sto provando a creare una struttura di classe nidificata per contenere i valori di un file di impostazioni gerarchico (json). La mia tendenza naturale sarebbe quella di nominare le proprietà come la classe nidificata (ad esempio, se ho una classe nidificata "Settings.Color", vorrei avere "public Color Color {get; set;}"), ma ovviamente questo non verrà compilato. Le mie opzioni sembrano essere quella di rinominare la classe o la proprietà, ad esempio, 'ColorInfo', ma ciò potrebbe portare a nomi sgradevoli per gli elementi nella gerarchia. – devuxer

+0

@devuxer: Non conosco un buon modo per risolvere questo problema, ma di solito non è un problema perché è considerato un cattivo odore rendere un tipo annidato pubblico. Se il tipo annidato non è pubblico, non hai mai una proprietà pubblica che espone il tipo in quel modo. Eviterei i tipi annidati; Li uso solo quando il tipo annidato è un dettaglio di implementazione privato del tipo esterno. –

3

Preferisco denominare enumerazioni utilizzando un nome seguito da Opzioni. Nel tuo caso:

SuitOptions 
RankOptions 

Dopo tutto, l'enumerazione è solo una serie di possibili opzioni, giusto?

Si dovrà quindi:

myCard.Suit = Card.SuitOptions.Clubs; 

che a mio parere ha un senso e si è ancora in grado di sapere quando si visualizza il testo se è poi enum o proprietà.

+1

Poiché un enum è un insieme di opzioni per definizione, sembra ridondante inserirlo nel nome. Sembra equivalente all'aggiunta di un suffisso -Enum, che le linee guida sullo stile dicono di non fare. –

+0

Sono d'accordo che l'aggiunta di -Enum suffisso è ridondante ma continuo a credere che -Opzioni sia una buona scelta perché: descrive cosa è quel tipo mentre solo "Suits" mi farà pensare ad una collezione di abiti e semplice "Suit" non è lavorando in questo contesto. –

+1

Esistono enumerazioni in cui si pensa -Opzioni è un suffisso inappropriato? Non riesco a pensare a dove * non * si adatta, il che penso sia il problema: è troppo generico. È come -Manager o -Helper su una classe. –

1

Sposterei le enumerazioni al di fuori della definizione della classe e userei lo spazio dei nomi per indicare che si riferiscono alle carte. In genere non mi piace l'enumerazione annidata all'interno delle classi. Hai ragione riguardo alla pluralità, però: singolare per enumerazioni regolari, plurale per enumerazioni di bandiere.

+0

Questa è un'altra opzione, ma si incontrano rapidamente problemi simili. Come dovrebbe essere chiamato lo spazio dei nomi? 'Card' è fuori, da allora sarebbe in conflitto con il nome della classe. Potresti annidarlo di un altro livello in uno spazio dei nomi 'Enums', immagino. Qualcosa come 'Core.Enums.Card' dove la classe stessa è' Core.Card'. Questa non è una cattiva opzione. –

1

Le tue enumerazioni sono fondamentalmente tipi di dati che hai definito. Non useresti "int" o "string" come nome di membro, quindi penso che sia ugualmente una cattiva idea usare i tuoi nomi enum e nomi di membri nel tuo caso.

+0

Quindi, come li chiameresti in questo caso * specifico *? 'int' e' string' sono cose molto generali, ma 'Suit' e' Rank' sono significativi solo nel contesto di una 'Card'. –

+0

Lo odio davvero quando una posizione filosofica perfettamente accettata viene inciampata dalla realtà pratica :) Nel mio schema di denominazione, uso sempre CamelCase per i miei nomi di proprietà, quindi la mia dichiarazione di proprietà sarebbe "tuta pubblica" che andrebbe bene . Se non puoi farlo, mi piace la tua idea di classe CardInfo. E CardSuit (come hai suggerito) o SuitOptions (di Victor) non sono male. Una volta tanto, immagino che la nomenclatura logica dei nostri oggetti non sia coerente con ciò che il compilatore ci permette di fare. – Ray

+0

Vero. Ci sono alcune opzioni decenti nelle risposte qui, ma nessuno si sente * ideale *. Mi sto appoggiando alla mia cosa 'CardInfo', ma lo farei, no? L'ho suggerito, dopo tutto. Penso che questa sia in gran parte una scelta di stile. –

3

Concordo con lo spostamento della definizione enum in un altro luogo.Attualmente, le enumerazioni sono visibili solo attraverso la carta, quindi se si voleva verificare la presenza di un asso, si avrebbe a che fare

if (card.CardSuit == Card.Suit.Ace) { } //need different name for Suit field 

Dove se si è spostato a una definizione autonoma, si potrebbe fare questo se l'hai fatta globale:

if (card.Suit == Suit.Ace) { } //no naming clashes, easier to read 
+1

I tipi/enumerazioni annidati dovrebbero essere evitati se saranno usati spesso. Ad esempio, sono difficili da scoprire tramite IDE/Intellisense perché anche quando si digita "Suit" non si otterrà la richiesta di aggiungere automaticamente "using", poiché è necessario accedere all'enum come "Card.Suit" come in Crazy's exmaple. Annidare un tipo solo se è utilizzato solo internamente all'interno della classe, è necessario esternamente molto raramente o viene utilizzato solo in scenari avanzati come l'ereditarietà della classe. – AaronLS

Problemi correlati