2013-08-30 6 views
6

Ho due classi che vorrei conservare in file separati.Come faccio a garantire che una Classe possa chiamare un metodo su un'altra Classe, ma non altre Classi possono chiamare quel metodo?

namespace GridSystem 
{ 
    public class Grid 
    { 
     public void AddItem(GridItem item) 
     { 
      item.InformAddedToGrid(); 
     } 
    } 
} 

namespace GridSystem 
{ 
    public class GridItem 
    { 
     public void InformAddedToGrid() 
     { 
      Debug.Log("I've been added to the grid"); 
     } 
    } 
} 

Come faccio a garantire che nessun'altra classe sia autorizzata a chiamare InformAddedToGrid?

Sto cercando di emulare gli spazi dei nomi Actionscript, che possono essere utilizzati su un metodo, al posto di pubblico, privato, interno, ecc. Non protegge esattamente il metodo, ma impone un ulteriore passaggio per includere lo spazio dei nomi prima che sia possibile accedere al metodo. C'è un approccio alternativo a questo in C#?

+0

Passare al metodo 'private'. – zsong

+3

Sembra che tu stia cercando l'equivalente C# di C++ 'amico'. Non penso che esista. – Mysticial

+0

Quando la griglia privata non può accedervi neanche. – Tseng

risposta

1

Non dovresti farlo, dovresti fare quello che suggerisce TGH, avere un'interfaccia pubblica per GridItem, e avere gridItem nidificato in Grid (quindi avere un metodo factory su Grid per creare elementi e usare la griglia Grid per averli in file separati).

Perché non c'è un modo di avere metodi amico (si può fare classi amico attraverso InternalsVisibleToAttribute)

È POTREBBE fare questo (ma non ...)

public partial class Grid 
{ 
    public void AddItem(GridItem item) 
    { 
     item.InformAddedToGrid(); 
    } 
}   

public class GridItem 
{ 
    public void InformAddedToGrid() 
    {     
     if (new StackTrace().GetFrame(1).GetMethod().DeclaringType != 
        typeof(Grid)) throw new Exception("Tantrum!"); 
     Console.WriteLine("Grid called in..."); 

    } 
} 

poi

var g = new Grid(); 
g.AddItem(new GridItem()); // works 
new GridItem().InformAddedToGrid(); // throws a tantrum... 
+0

Per curiosità stai dicendo che normalmente non usi 'partial' o ti riferivi solo all'intero esempio quando hai detto "ma non ..."? – jmstoker

+0

L'inlining del metodo può ucciderti camminando in questo modo. E l'esecuzione di un tale metodo sarà terribile. –

+0

Quindi ti stai riferendo a 'if (new StackTrace(). GetFrame (1) .GetMethod(). DeclaringType! = typeof (Grid)) lancia una nuova Exception (" Tantrum! ");' Come una cattiva idea? – jmstoker

5

Se GridItem stesso può essere nascosto anche al mondo esterno, prenderei in considerazione l'inserimento di GridItem in Grid come classe nidificata. In questo modo non sarà visibile al di fuori della classe

http://www.codeproject.com/Articles/20628/A-Tutorial-on-Nested-Classes-in-C

+0

in base alla sua API sulla griglia, ha il concetto di esporre GridItem al mondo esterno, il che ha senso –

+1

Ok vorrei mettere un'interfaccia su GridItem senza il metodo definito e quindi esporlo al mondo esterno. In questo modo puoi esporre ciò che hai bisogno di esporre dalla classe originale GridItem al mondo esterno, ma nascondere il metodo in questione – TGH

1

Una risposta davvero brutto sarebbe quello di rendere riflessione privata e uso.

Un'altra brutta risposta sarebbe quella di fare un'eccezione se il chiamante ha torto.

Entrambe sono molto più lente da eseguire rispetto a una normale chiamata.

Non penso che ci sia una buona risposta. C# non ha amici.

0

IMHO la risposta è semplice: modificatori di accesso sono solo lì per ricordare il programmatore del intento di come dovrebbe essere pubblica/privata una classe. Attraverso la riflessione puoi sollevare queste barriere.

L'utilizzo che fai di una classe è tutto nelle tue mani: se la tua classe è significa che lo deve essere utilizzato in un unico posto, fallo così. Se mai, se una classe ha un modo speciale di essere utilizzata, documentala - mettila nei commenti XML.

Detto questo, in questo esempio specifico avrei creduto dal momento che la GridItem non si fa aggiungere alla rete, non è il suo compito di informare su di esso (what if "io non sono stato aggiunto alla rete "?). Penso che InformAddedToGrid appartenga da qualche parte nella tua classe Grid come metodo private, dove c'è un concetto di che aggiunge un elemento ... supponendo che sia ciò che fa davvero AddItem(GridItem).

+0

Anche se InformAddedToGrid è un membro di Grid, GridItem avrà ancora accessor pubblici che desidero riservati per le classi nello spazio dei nomi di GridSystem. Ad esempio, InformAddedToGrid potrebbe modificare i valori X e Y di GridItem. Voglio che le classi GridSystem siano in grado di scrivere i valori X e Y e le classi esterne a GridSystem solo per leggere i valori X e Y. – Unknown123

+0

La documentazione non può che andare fino a qui IMHO. Di solito sono Pythonic per quanto riguarda l'accesso, ma mi piacerebbe essere più infallibile in questo caso particolare (è per questo che ho fatto la domanda). – Unknown123

0

Puoi farlo come suggerito da TGH, con classi annidate, tranne il contrario.Nest Grid all'interno di GridItem e rendere privato InformAddedToGrid. Qui uso una classe di base annidata in modo che l'API pubblica possa rimanere la stessa. Si noti che nessuno all'esterno dell'assembly può ereditare da GridBase perché il costruttore è interno.

public class GridItem 
{ 
    public class GridBase 
    { 
     internal GridBase() { } 

     public void AddItem(GridItem item) 
     { 
      item.InformAddedToGrid(); 
     } 
    } 

    private void InformAddedToGrid() 
    { 
     Debug.Log("I've been added to the grid"); 
    } 
} 

public class Grid : GridItem.GridBase { } 

Un'altra opzione è quella di avere GridItem esplicitamente implementare un'interfaccia interna. In questo modo nessuno all'esterno dell'assembly può utilizzare l'interfaccia per nome e pertanto non può chiamare InformAddedToGrid.

public class Grid 
{ 
    public void AddItem(GridItem item) 
    { 
     ((IGridInformer)item).InformAddedToGrid(); 
    } 
} 

public class GridItem : IGridInformer 
{ 
    void IGridInformer.InformAddedToGrid() 
    { 
     Debug.Log("I've been added to the grid"); 
    } 
} 

internal interface IGridInformer 
{ 
    void InformAddedToGrid(); 
} 
Problemi correlati