2009-07-21 2 views
8

Questa domanda è in realtà un pò inutile, ma io sono solo curioso:Perché viene compilata questa dichiarazione della classe C#?

questo:

public sealed class MyClass 
{ 
    protected void MyMethod(){} 
} 

compila, ma dà un avvertimento

mentre questo:

public sealed class MyClass 
{ 
    public virtual void MyMethod(){} 
} 

doesn Compilare Solo per pura curiosità, c'è una ragione per questo?

+9

Darei una risposta, ma per qualche ragione penso che JS ne avrebbe uno migliore. Hmm. –

+2

A quanto pare JS sta dando priorità a SO a THREAD_PRIORITY_BELOW_NORMAL - rapidamente tutti possono ottenere le risposte mentre è ancora possibile! – cfeduke

+0

+1 buona domanda, non mi sono mai imbattuto in questa causa, non ho mai nemmeno pensato di farlo ... perché non ha senso! –

risposta

4

L'unica ragione per cui posso pensare è che a volte è necessario scrivere metodi protetti per scavalcare altri metodi protetti. Il linguaggio potrebbe sono stati progettati per permettere questo:

protected override void Foo() 

ma non questa

protected void Foo() 

ma che avrebbe potuto essere visto per essere un po 'difficile da seguire - è il assenza di override che lo rende inutile, mentre nel caso di

public virtual void Foo() 

è il presenza di virtual che è inutile. La presenza di qualcosa di "sbagliato" è probabilmente più facile da capire rispetto all'assenza di qualcosa di utile.

In questo caso, essere virtuale può anche avere implicazioni sulle prestazioni, mentre la creazione di qualcosa di protetto anziché privato probabilmente non lo è, quindi è un po 'più grave.

Queste sono solo ipotesi, ma davvero - se saremo davvero fortunati, Eric Lippert darà una risposta più definitiva. E 'quello che vuoi, non mi :)

Miglior risposta: trattare gli avvisi come errori e loro sono equivalenti comunque;)

+0

Ooh, buon punto, non pensavo al caso in cui la classe sigillata in questione eredita da una base e sovrascrive un membro protetto.Questa potrebbe benissimo essere una ragione: in realtà ho mandato un'email a Eric ieri sera, e lui ha risposto dicendo che ci guarderà dentro e tornerò da me, terrò questa domanda aperta fino a quando lo fa. Grazie Jon – BFree

+1

Ehi, sto solo indovinando, ragazzi. Le note sul design del linguaggio documentano che la decisione di fare l'introduzione un metodo virtuale in un tipo sigillato è stato commesso un errore il 18 ottobre 1999, ma non fornisce alcuna giustificazione per la decisione.Non riesco a trovare da nessuna parte nelle note che giustifichi il motivo per cui l'introduzione di un nuovo membro protetto dovrebbe essere legale. ipotesi migliore: probabilmente si trattava semplicemente di una svista nella prima versione, e poi è diventata una modifica irrisolta per risolvere il problema. –

+2

@Eric: Interesse, dove si trova questo nelle specifiche? Non sembra essere nel parte introduttiva del 10,6 wh ich elenca le regole per le combinazioni valide di modificatori. Ho pensato di trovare la regola e poi vedere se è menzionata in una delle specifiche annotate ... e ora non riesco a trovare la regola. Non riesco a vederlo neanche nel 10.6.3. –

12

virtual viene utilizzato per dichiarare un metodo/proprietà "override-able".

sealed viene utilizzato per dichiarare che la classe non può essere ereditata da.

Quindi un metodo virtuale in una classe sigillata non può mai essere sovrascritto, poiché la classe non potrebbe mai essere ereditata. Semplicemente non ha senso.

protected influisce sull'accesso a un membro, non lo dichiara "override-able" come virtuale (anche se viene spesso utilizzato in questo modo) e di conseguenza non è contraddittorio.

+0

Sì, lo capisco. Notate che non ho chiesto "perché non si compila", piuttosto ho chiesto perché questo compila. Allo stesso modo in cui il virtuale non ha senso in una classe chiusa, protetto non vede né quello che sto dicendo? – BFree

+0

I * think * l'OP si rende conto che 'sealed' non ha senso con' virtuale' o 'protetto'. Si sta chiedendo perché un oggetto "non abbia senso" è permesso, mentre un altro no. –

+0

@ 280Z28 Sono d'accordo ... Abbiamo bisogno di Skeeter per spiegarcelo. –

4

Non riesco a vedere una buona ragione per questo. Il MyMethod protetto può essere chiamato da MyClass, ma non verrà mai chiamato da una classe derivata (perché MyClass è sigillato). La versione virtuale può anche essere chiamata direttamente da MyClass, ma è illegale per il metodo avere una sostituzione perché non è possibile derivare una classe da MyClass ...

2

Una classe sigillata può avere membri protetti tramite ereditarietà . Quando un metodo fa parte di una classe, non importa come sia arrivato quel metodo.

Nel primo caso, con il metodo protetto sulla classe sigillata, è lo stesso come se la classe sigillata ereditasse un metodo protetto. Quindi compila.

Per curiosità, qual è esattamente l'avviso?

+0

L'avviso è: "avviso CS0628: 'SimpleTestApp.Class1.foo()': nuovo membro protetto dichiarato in classe sealed." –

1

Una classe sigillata non può essere sottoclassata, quindi virtuale non è un'opzione. Quindi errore.

Questo primo è un po 'sciocco ma valido, quindi avvertimento.

2

L'errore è:

CS0549: 'funzione' è un nuovo membro virtuale nella classe chiusa 'classe'.

Prima di tutto, nonostante il fatto che in realtà non ha senso per includere nuovi protected o virtual membri in una classe sealed, il CLI¹ non permetterlo. La CLI consente anche di chiamare i membri di una classe sigillata usando l'istruzione IL callvirt, anche se un compilatore può liberamente sostituirlo con l'istruzione call.

Attualmente, non riesco a trovare nulla in ECMA-334 (C# Language Specification) che richiede che il compilatore emetta l'errore precedente. Sembra che l'implementazione di Microsoft abbia aggiunto l'errore solo perché non ha senso includere nuovi membri virtuali in una classe sigillata.

¹La CLI è una macchina virtuale e il compilatore C# emette il codice byte che viene eseguito su di esso. Quasi ogni concetto che è illegale nella CLI è anche illegale in C# per questo motivo - ma questo è un caso in cui C# fa un piccolo extra (non che sia un problema).

Modifica: Sembra che i post che vengono contrassegnati spiegano perché lo non ha senso scrivere nell'OP. Ma riguardo a quale regola l'errore del compilatore è apparso sbagliato.

0

Immagino che il compilatore esegua alcune ottimizzazioni con classi sigillate che sono impossibili se si dichiara un metodo virtuale - "non avere un vtable" sembra un candidato probabile.

Questa è solo una supposizione, però.

0

Come sigillato When applied to a class, the sealed modifier prevents other classes from inheriting from it.

qui sto cercando di spiegare uno per uno :

public sealed class MyClass 
{ 
    protected void MyMethod(){} 
} 

ti dà avvertimento, perché in pratica si fanno alcun senso, perché dopo aver dichiarato una classe come sigillato non si può eredita e come metodo è protected quindi non è possibile accedervi dall'esterno della classe usando il suo objec t (e tieni anche presente che non puoi creare una classe figlia di questo in modo da non poter usare questo metodo anche con quel trucco). Quindi praticamente non ha senso farlo diventare protected così il compilatore ti dà un avvertimento ma se lo fai come public o internal quindi non ti darà errore perché è utile in quel caso.

ora la seconda:

public sealed class MyClass 
{ 
    public virtual void MyMethod(){} 
} 

come si sigillati classe e ora si sta procedendo il tuo metodo come virtuale in modo indirettamente si stanno dando un'offerta a qualcuno di ignorare e che può essere possibile solo per via ereditaria e qui arriva il problema. Quella classe è sigillata, quindi non puoi eseguire l'ereditarietà con questa classe. Ecco perché con virtual dà errore.

spero che possa aiutarti a capire.

per riferimento http://msdn.microsoft.com/en-us/library/88c54tsw.aspx

0

Dichiarare un nuovo membro protetto implica un intento di condividere quel membro con le classi discendenti. Una classe sigillata non può avere discendenti, quindi dichiarare un nuovo membro protetto è un po 'un ossimoro, proprio come dichiarare un nuovo metodo virtuale in una classe sigillata.

Per quanto riguarda il motivo per cui virtuale produce un errore mentre protetto produce solo un avviso, posso solo ipotizzare che forse ha a che fare con il fatto che i nuovi metodi virtuali richiedono al compilatore di costruire strutture dati per il tipo (un vtable), mentre i nuovi membri protetti hanno solo un set di flag di accesso - nessuna nuova struttura di dati. Se al compilatore è proibito creare un vtable per una classe sigillata, cosa dovrebbe fare se incontra un nuovo metodo virtuale? Fallire la compilazione Un nuovo metodo protetto in una classe sigillata è inutile ma non richiede al compilatore di avventurarsi in un territorio proibito.