2011-01-19 10 views
13

O perché è la seguente impossibile:Perché il nome di un membro di una classe non può essere uguale a una delle sue classi nidificate?

class Material 
{ 
    class Keys 
    { 
     ... 
    } 

    Material.Keys Keys { get; set; } // Illegal 
} 

non vedo ogni possibile ambiguità. Quando si accede per istanza, restituire la proprietà. Quando accedi in modo statico, restituisci la classe. O mi sta sfuggendo qualcosa?

Non sto chiedendo una "correzione" (so che potrei chiamarlo in modo diverso, come MaterialKeys o simili), ma più di una ragione tecnica dietro questo limite.

+0

Questo è lo scenario che vorrei vedere il lavoro. Ha perfettamente senso che io voglia dichiarare una classe interiore e dichiarare un membro di quel tipo nella classe esterna. La classe esterna contiene esattamente un'istanza di una cosa che definisce. Quella cosa non è progettata per funzionare in nessun altro contesto, quindi ha senso che sia la classe sia il membro si trovino nello stesso spazio dei nomi (ovvero la classe esterna è lo spazio dei nomi ... non la parola chiave dello spazio dei nomi C#). Per avere sia la classe che il membro nella stessa classe (esterna), devo nominarne uno in modo diverso.Ma quale voglio avere un nome strano? – steve

risposta

12

Ma Immagina di avere questo:

class Material 
{ 
    class Keys 
    { 
     ... 
    } 

    static Material.Keys Keys = new Keys(); 
} 

Ora entrambi sono a portata "statico". Ora, il compilatore può disambiguare in tutti i casi? In caso contrario, questo non può essere consentito.

Suppongo che sia possibile che la disambiguazione funzioni per campi/proprietà/metodi statici e non per membri di esempio. O viceversa.In tal caso, vorresti che le specifiche della lingua consentissero a un membro di un'istanza di avere lo stesso nome di una classe interna, ma di non utilizzarlo per la statica? Sarebbe solo confuso.

Ma poi, avere un membro che corrisponde al nome di una classe interna è comunque piuttosto confuso.

+0

Molto ragionevole ... motivo. Grazie. – Lazlo

+0

Non direi che confonderebbe un membro con il suo nome tipo o il nome di una classe interna. Sono usati in modo diverso: uno è una classe e uno è un nome di proprietà. Non ci dovrebbe essere confusione lì. Ciò non annulla il primo punto, che è azzeccato. Non c'è modo di distinguere tra la classe 'Material.Keys' e il membro statico' Material.Keys'. Questo è praticamente un interruttore per questo meccanismo. Quello che vorrei dire è che se si viene presentati a questa situazione, dovrebbero considerare se sono il più descrittivi possibile nella loro denominazione. 'KeyCollection' invece di' Keys' – crush

+0

"Volete che le specifiche della lingua consentano a un membro di un'istanza di avere lo stesso nome di una classe interna, ma non lo consentono per le statiche?" Sì, lo farei. – drowa

2

Poiché la classe nidificata Keys è un membro di Material come la proprietà Keys. Hai due membri chiamati Keys.

Allo stesso modo non si possono avere due proprietà chiamato la stessa cosa:

public class Bar 
{ 
    private bool Foo { get; set; } 
    private string Foo { get; set; } 
} 

Quando si accede Foo quale stai tentando di accedere?

public class Material : Keys 
{ 

    private Keys K { get; set; } 
} 

public class Keys 
{ 

} 

Funziona bene, ma probabilmente non è quello che cerchi.

+0

Concordo sul fatto che non è possibile avere membri di istanza definiti con lo stesso nome, ma perché non si può avere un membro statico e un membro di istanza con lo stesso nome? Non puoi assolutamente confonderli. "Material.Keys" non è lo stesso di "new Material(). Keys". – Lazlo

+1

Cosa succede quando si accede a 'Keys' dall'interno di' Membri'? Qual è, statico o membro? 'Materiale pubblico() {Chiavi. // quale? } ' –

+1

Non so per te, ma il mio compilatore non mi ha mai permesso di assegnare una classe nidificata da un'istanza. Quindi 'new Material() {Keys}' sarebbe sicuramente quello istanziato. – Lazlo

4

Lei ha detto,

quando vi si accede da esempio, restituire la proprietà. Quando accedi in modo statico, restituisci la classe.

Ma cosa succede se dici solo Keys da qualche parte dentro Material? Si tratta di un accesso statico o di istanza? Si riferisce alla proprietà Keys o al tipo nidificato Keys? In realtà è ambiguo.

Per esempio,

class Material 
{ 
    class Keys 
    { 
     public static int Length; 
    } 

    string Keys { get; set; } 

    public void Process() 
    { 
     // Does this refer to string.Length (via property Keys) 
     // or Material.Keys.Length? It actually refers to both. 
     Console.WriteLine(Keys.Length); 
    } 
} 

Come sottolineato nei commenti, questa non è tutta la storia; ma quasi. È valido per avere una proprietà denominata Color del tipo Color e non c'è nessuno scontro:

public Color Color { get; set; } 

Color.FromName(...) // refers to static method on the type ‘Color’ 
Color.ToString()  // refers to instance method on the property’s value 

Ma questo è facile da risolvere, semplicemente perché le cose nel campo di applicazione corrente conquistare le cose in ambiti più esterni:

public class MyType { public string FromName(string name) { return null; } } 
public MyType Color; 

Color.FromName(...) // unambiguously refers to MyType::FromName(string) 
         // via the property Color 

Non è così semplice nell'esempio: la classe nidificata Keys e la proprietà Keys sono nello stesso ambito (hanno lo stesso tipo di dichiarazione). Come decidi quale dare la priorità? E anche se decidessi di dare una priorità a loro, questo sarebbe utile solo marginalmente perché potresti avere solo due cose con lo stesso nome, e una dovrebbe essere statica e l'altra.

+1

La parola chiave "this" potrebbe risolvere questo problema. Ricevo un'eccezione di ambiguità quando eseguo "this.Keys.Length". – Lazlo

+3

Questa non è la storia completa; semplicemente con questo ragionamento, 'Colore colore pubblico {get; impostato; } 'dovrebbe anche essere illegale. – Ani

+0

@Ani - quando si usa 'Color' in quel contesto, verrà utilizzata prima l'istanza del membro. Usando il nome completo di 'System.Drawing.Color' darai accesso alla classe' Color'. Questo non è lo stesso problema dal momento che la tua classe ha ancora un solo membro chiamato 'Color', semplicemente per usare un'altra classe chiamata' Color'. Distinguere tra di loro è banale. –

3

La mia risposta affronta la domanda da una prospettiva leggermente diversa, rispetto alle altre domande. Delle due seguenti dichiarazioni in una specifica di linguaggio C#:

The same identifier may not be used in different definitions within one scope 

e

The same identifier may not be used in different definitions within one scope, unless it is probably impossible for any ambiguity to arise when the identifier is used 

, il primo è molto più semplice.

La semplicità è un obiettivo chiave nella progettazione della lingua, perché i linguaggi più semplici sono più facili da implementare per gli autori di compilatori e interpreti, più semplici da generare e manipolare, più facili da apprendere per i principianti e più facili da comprendere per i programmatori. Quando si considera qualsiasi caratteristica linguistica, la complessità che tale caratteristica aggiunge alla lingua deve essere considerata negativa e deve quindi essere bilanciata da una misura di utilità almeno equivalente. Come hai affermato tu stesso, consentire ciò non aggiungerebbe alcuna funzionalità reale (dato che è così facile da aggirare), quindi non c'era alcun motivo valido per complicare ulteriormente le specifiche di C# includendolo.

+0

Sono d'accordo che è _easy_ per risolvere il problema - nominare il membro diverso dal tipo. Ma sto lottando con quello. Uno otterrà il nome significativo e l'altro otterrà un nome strano - un derivato che è diverso solo perché deve essere diverso ... non perché nominarlo in modo diverso rende il codice più leggibile. Ad esempio, se nomino il campo membro, quale nome attribuisco al tipo? FieldType, FieldClass, _Field? Nulla di ciò che ho messo sulla parola "Campo" fa altro che chiedere al lettore: perché il nome del tipo è diverso dal nome della classe? – steve

10

"Tutto ciò che non è ambiguo dovrebbe essere legale" NON è assolutamente un principio di progettazione del linguaggio C#. Il linguaggio C# è progettato per essere un linguaggio "di qualità"; cioè, le regole della lingua dovrebbero gettarti in una buca piena di codice chiaramente corretto, e devi lavorare per arrampicarti fuori dalla buca per trasformarlo in codice errato. L'idea che "qualunque cosa non sia ambigua dovrebbe essere legale" funziona nella maggior parte dei casi direttamente contro il concetto di un linguaggio di "fossa di qualità".

Inoltre, la tua idea di dover fornire una giustificazione per non eseguire una funzione è al contrario. Non è necessario per fornire una giustificazione per non facendo una funzione. Piuttosto, le caratteristiche proposte da devono essere giustificate dimostrando che i loro benefici superano i loro enormi costi. Le caratteristiche sono molto costose e abbiamo un budget limitato; dobbiamo solo fare le migliori caratteristiche per offrire i loro benefici ai nostri clienti.

La funzionalità proposta consente la facile produzione di codice fragile e confuso; aiuta a trasformare il C# in un linguaggio "buca della disperazione" invece di un linguaggio "buca della qualità". Le caratteristiche che aggiungono fragilità e confusione alla lingua devono aggiungere un enorme vantaggio per compensare tali costi. Qual è secondo te l'enorme vantaggio che questa funzione aggiunge al linguaggio che giustifica i suoi costi?

Se la risposta è "non c'è tale vantaggio", ora sai perché la lingua non ha questa caratteristica: perché peggiora la lingua, netta.

Se c'è un vantaggio, sono felice di considerare i suoi meriti per ipotetiche versioni future della lingua.

+1

Non sottintende che ci sia stato un grande beneficio. Mi sono semplicemente interrogato sulla semantica della lingua. Ovviamente StackOverflow non è solo un "pozzo di comprensione". – Lazlo

+4

Penso che ci sia beneficio per la funzionalità. Una classe che può definire una classe e un membro di quel tipo crea una buona incapsulamento. Capisco il problema dell'ambiguità e mi chiedo come possa essere risolto. Forse viviamo con un codice più ambiguo (anche se questo non è il modo C#). O aggiungiamo qualcosa alla lingua per consentire questa funzione. Forse una sintassi speciale per una classe interiore che consente questo.Non so cosa sarebbe, ma qualcosa che elimina il problema ambiguo. – steve

+1

Uno degli usi convenienti è quando si dispone di una classe che espone un enumerazione che riguarda solo il suo contesto. Ad esempio, supponiamo di avere un 'enum ReportType'. Ora potrei desiderare una proprietà denominata ReportType di tipo "enum ReportType". Preferirei non ingombrare quell'enum al di fuori di questa classe come mi piacerebbe fare riferimento ad esso come "Foo.ReportType.MyFancyReport". Questo tipo di situazioni sono abbastanza comuni durante la creazione di classi di modelli. – arviman

Problemi correlati