2014-09-18 16 views
5

Immagina di avere una classe definita come segue.Riferimenti C#; Tenere nascosti i membri

public class SomeClass 
{ 
    public Manager m { get; protected set; } 
    public SpecialData data { get; protected set; } 

    //More methods and member code here... 
} 

Ho bisogno che la mia classe manager sia in grado di modificare il riferimento impostato per il membro SpecialData. Potrei farlo con un doppio puntatore o classi di amici in C++, ma quell'opzione non è disponibile in C#, purtroppo. Come mantenere protetto SpecialData da utenti esterni che lo impostano, consentendo comunque il controllo della classe Manager su impostazione? Potrei farlo con la parola chiave interna, ma ciò non sembra incredibilmente sicuro o pulito ...

Qualsiasi aiuto è molto apprezzato.

+2

Potresti spiegare perché ritieni che 'internal' non sia sicuro o non sia pulito? Vuoi dire che è così in generale, o stai suggerendo che non è adatto al tuo design specifico? Direi che è una soluzione perfetta qui, anche se la verità è che non sono sicuro del perché tu voglia fare questo in primo luogo. – decPL

risposta

4

Se la classe manager è parte dello stesso insieme come SomeClass, rendendo il membro internal consentirebbe per le classi nello stesso assieme per accedere al setter:

public SpecialData data { get; protected internal set; } 

Questo è simile all'utilizzo friend in C++, con l'eccezione che "l'amicizia" è estesa a tutti i membri dello stesso assembly.

Se il gestore fa parte di un pacchetto diverso, è possibile utilizzare l'attributo InternalsVisibleTo sull'assieme. In questo caso, tuttavia, è necessario firmare l'assemblea a cui si estende l'amicizia per evitare tentativi di accesso al setter da codice non autorizzato.

+0

Ho menzionato la parola chiave interna. Mi rendo conto che è un'opzione, ma è buona? Ero preoccupato che qualcuno potesse essere un difetto di progettazione. In caso contrario, funzionerebbe sicuramente. Questo è un grande progetto, quindi accoppiarli insieme potrebbe essere un grosso problema ... – guitar80

+0

@ user3812869 Sì, 'internal' è una buona opzione. È progettato per situazioni come questa, quando si progetta un pacchetto di classi correlate che condividono la conoscenza della rappresentazione interna. .NET prende precauzioni per non divulgare questa conoscenza al di fuori della tua biblioteca. Presumibilmente, non c'è modo di proteggere il tuo codice da persone che possono modificare il tuo assembly. – dasblinkenlight

+0

Beh, in tal caso proverò a risolvere i nodi. Il design mi sembra leggermente strano. In realtà ho uno StateManager e uno Sprite contenuti in un oggetto di gioco. Lo StateManager contiene Stati. Questi stati devono modificare il campo Sprite. – guitar80

1

Creare una classe che eredita SomeClass e sembra un po 'questo:

internal class SomeClassDecorator : SomeClass 
{ 
    public void SetSpecialData(SpecialData value) 
    { 
     data = value; 
    } 
} 

Protetta significa che è a disposizione dei derivati ​​della classe, e dal momento che SomeClass non è sigillato, semplicemente si può derivare da esso e fare qualunque cosa ti serva. È quindi possibile utilizzare questo decoratore anziché lo stesso SomeClass. Inoltre, puoi avere tutti i decoratori di cui hai bisogno, ognuno dei quali gestisce il suo speciale SpecialData.

1

Potrebbe non essere esattamente quello che stai cercando, ma la parola chiave internal descritta here può governare l'accesso all'interno dello stesso assembly; sembra uno scopo simile alla parola chiave friend in C++.

1

È possibile effettuare la proprietà internal. Ciò renderà la proprietà visibile solo all'interno dello stesso assieme. Opzionalmente è possibile utilizzare lo InternalsVisibleToAttribute per consentire a determinati assiemi di accedere a tale proprietà.

Un altro approccio è l'uso di un interface nascondere tale proprietà:

public interface ISomeClass 
{ 
    Manager m { get; } 
    SpecialData data { get; set; } 
} 

public class SomeClass : ISomeClass 
{ 
    public Manager m { get; protected set; } 
    SpecialData ISomeClass.data { get; set; } 

    //More methods and member code here... 
} 

In questo modo, data è visibile solo da interfaccia di riferimento.

Quindi questo non funziona:

SomeClass c = new SomeClass(); 
c.data; 

ma questo:

ISomeClass c = new SomeClass(); 
c.data; 
3

cosa sulla creazione di un evento sulla classe Manager, qualcosa di simile a un evento RequestChangeSpecialData. Lo Manager attiva l'evento e lo SomeClass cambierà l'istanza SpecialData.

public class SomeClass 
{ 
    private Manager _m; 

    public Manager M 
    { 
     get { return _m} 
     set 
     { 
      // register/unregister event on property assignment 
      if(_m != null) 
       _m.RequestChangeSpecialData -= RequestChangeSpecialData; 

      _m = value; 

      if(_m != null) 
       _m.RequestChangeSpecialData += RequestChangeSpecialData; 

     } 
    } 

    public SpecialData Data { get; private set; } 

    private void RequestChangeSpecialData(object sender, ChangeSpecialDataEventArgs e) 
    { 
     // set the new reference 
     Data = e.SpecialData; 
    } 

} 

public class Manager 
{ 
    public void DoSomething() 
    { 
     // the manager class wants to do something, and wants to change the SpecialData instance.., so it fires the event RequestChangeSpecialData 


     SpecialData data = new SpecialData(); 

     // if the event is bound. 
     if(RequestChangeSpecialData != null) 
      RequestChangeSpecialData(this, new ChangeSpecialDataEventArgs(data)); 
    } 

    public event EventHandler<ChangeSpecialDataEventArgs> RequestChangeSpecialData; 
} 

public class ChangeSpecialDataEventArgs : EventArgs 
{ 
    public SpecialData Data {get; private set; } 

    public ChangeSpecialDataEventArgs(SpecialData Data) 
    { 
     Data = data; 
    } 
} 

il fitness (scritto nel blocco note)

Ora il Manager è in grado di modificare la proprietà SpecialData. In questo modo il gestore non dipende dall'interfaccia o dall'assieme SomeClass.

+0

stava per postare l'opzione evento –