Domanda

Ho una lezione Bar con un campo privato contenente il tipo di riferimento Foo.vorrei esporre Foo in un immobile pubblico, ma non voglio che i consumatori dell'immobile possano alterarsi Foo...Dovrebbe tuttavia essere modificabile internamente da Bar, cioè.Non posso entrare nel campo readonly.

Quindi quello che vorrei è:

      private _Foo;

      public Foo 
      { 
         get { return readonly _Foo; } 
      } 

...che ovviamente non è valido.Potrei semplicemente restituire un clone di Foo (ammesso che lo sia IClonable), ma questo non è ovvio per il consumatore.Dovrei cambiare il nome della proprietà in FooCopy??Dovrebbe essere un GetCopyOfFoo metodo invece?Cosa considereresti la migliore pratica?Grazie!

È stato utile?

Soluzione

Sembra che stai dopo l'equivalente di "const" da C ++. Questo non esiste in C #. Non c'è modo di indicare che i consumatori non possono modificare le proprietà di un oggetto, ma qualcos'altro può (assumendo che i membri mutanti sono pubblici, ovviamente).

Si potrebbe restituire un clone del Foo come suggerito, o, eventualmente, una vista sul Foo, come ReadOnlyCollection fa per collezioni. Naturalmente se si potrebbe fare Foo un tipo immutabile, che renderebbe la vita più semplice ...

Si noti che c'è una grande differenza tra fare il campo di sola lettura e rendendo l'oggetto stesso immutabile.

Al momento, il tipo stesso potrebbe cambiare le cose in entrambi i modi. Si potrebbe fare:

_Foo = new Foo(...);

o

_Foo.SomeProperty = newValue;

Se si ha solo bisogno di essere in grado di fare il secondo, il campo potrebbe essere di sola lettura, ma avete ancora il problema delle persone che vanno a prendere la proprietà di essere in grado di mutare l'oggetto. Se ha solo bisogno di fare il primo, e in realtà Foo è o già immutabile o potrebbe essere fatto immutabile, si può solo fornire una proprietà che ha solo il "getter" e non avrete problemi.

E ' molto importante che si capisce la differenza tra la modifica del valore del campo (per renderlo riferimento a una diversa istanza) e modificando il contenuto dell'oggetto che il campo si riferisce a.

Altri suggerimenti

Purtroppo, non c'è modo semplice intorno a questo in C # in questo momento. Si potrebbe estrarre la "sola lettura parte" del Foo in un'interfaccia e lasciate che il vostro ritorno proprietà che, invece di Foo.

Esecuzione di una copia, un ReadOnlyCollection, o avere Foo essere immutabili sono di solito i tre percorsi migliori, come hai già ipotizzato.

A volte preferisco metodi, invece di proprietà ogni volta che sto facendo qualcosa di più significativo che semplicemente restituire il campo sottostante, a seconda di quanto lavoro è coinvolto. I metodi implicano che qualcosa sta succedendo e raccogliere più di una bandiera per i consumatori quando si sta utilizzando l'API.

"Clonare" gli oggetti Foo che ricevi e restituisci è una pratica normale chiamata copia difensiva.A meno che non vi sia qualche effetto collaterale invisibile alla clonazione che sarà visibile all'utente, non c'è assolutamente alcun motivo per NON farlo.Spesso è l'unico modo per proteggere i dati privati ​​interni delle tue classi, specialmente in C# o Java, dove l'idea C++ di const Non è disponibile.(IE, deve essere fatto per creare correttamente oggetti veramente immutabili in questi due linguaggi.)

Giusto per chiarire, i possibili effetti collaterali sarebbero cose come il tuo utente (ragionevolmente) che si aspetta che l'oggetto originale venga restituito, o qualche risorsa detenuta da Foo che non verrà clonata correttamente.(In tal caso, cosa sta facendo implementando IClonable?!)

Se non si vuole a chiunque di pasticciare con il tuo stato ... non esporla! Come altri hanno detto, se qualcosa deve vedere il tuo stato interno, fornire una rappresentazione immutabile di esso. In alternativa, i clienti ottengono per dire si di fare qualcosa (Google per "dire non chiedere"), invece di fare da soli.

Per chiarire il commento di Jon Skeet si può fare una vista, che è un classe wrapper immutabile per il mutabile Foo. Ecco un esempio:

class Foo{
 public string A{get; set;}
 public string B{get; set;}
 //...
}

class ReadOnlyFoo{
  Foo foo; 
  public string A { get { return foo.A; }}
  public string B { get { return foo.B; }}
}

Si può effettivamente riprodurre il comportamento di C ++ const in C # - devi solo farlo manualmente

.

Qualunque sia Foo è, l'unico modo in cui il chiamante può modificare il suo stato è chiamando i metodi su di esso o impostazione delle proprietà.

Per esempio, Foo è di tipo FooClass:

class FooClass
{
    public void MutateMyStateYouBadBoy() { ... }

    public string Message
    {
        get { ... }
        set { ... }
    }
}

Quindi nel tuo caso, sei felice per loro di ottenere la proprietà Message, ma non impostarlo, e tu sei sicuramente non contento li chiama quel metodo.

Quindi, definire un'interfaccia che descrive quello che sono autorizzati a fare:

interface IFooConst
{
    public string Message
    {
        get { ... }
    }
}

Abbiamo lasciato il metodo mutante e solo lasciato nel getter sulla proprietà.

Quindi aggiungere che si interfacciano alla lista base del FooClass.

Ora nella tua classe con la proprietà Foo, si dispone di un campo:

private FooClass _foo;

E un getter proprietà:

public IFooConst Foo
{
    get { return _foo; }
}

Questa riproduce sostanzialmente a mano esattamente ciò che la parola chiave ++ const C avrebbe fatto automaticamente. In termini pseudo-C ++, un riferimento di tipo const Foo & è come un tipo generato automaticamente che include solo quei membri Foo che sono stati contrassegnati come membri const. Traducendo questo in qualche versione teorica futuro del C #, che ci si dichiara FooClass in questo modo:

class FooClass
{
    public void MutateMyStateYouBadBoy() { ... }

    public string Message
    {
        get const { ... }
        set { ... }
    }
}

In realtà tutto quello che ho fatto è fusa le informazioni in IFooConst di nuovo in FooClass, etichettando l'unico membro al sicuro con una nuova parola chiave const. Quindi, in un certo senso, l'aggiunta di una parola chiave const non aggiungerebbe molto alla lingua oltre ad un approccio formale a questo modello.

Poi, se si ha un riferimento a un oggetto const FooClass:

const FooClass f = GetMeAFooClass();

Si sarebbe solo in grado di chiamare i membri const su f.

Si noti che se la definizione FooClass è pubblico, il chiamante potrebbe lanciare un IFooConst in un FooClass. Ma possono farlo in C ++ troppo -. Si chiama "gettato via const" e coinvolge un operatore speciale chiamato const_cast<T>(const T &)

C'è anche la questione di interfacce non essere molto facile da evolversi tra le versioni del vostro prodotto. Se una terza parte può implementare un'interfaccia si definisce (che sono liberi di fare se si può vedere), allora non si può aggiungere nuovi metodi ad essa nelle versioni future senza richiedere altri ricompilare il loro codice. Ma questo è solo un problema se si sta scrivendo una libreria estendibile per gli altri a costruire. Forse una caratteristica const incorporato risolverebbe questo problema.

Stavo pensando a cose di sicurezza simili. C'è probabilmente un modo. Abbastanza chiaro, ma non a breve. L'idea generale è abbastanza semplice. Tuttavia ho sempre trovato un certo senso intorno in modo mai testato. Ma si potrebbe controllare -. Forse funzionerà per voi

Questa è pseudo codice, ma spero idea alla base è chiaro

public delegate void OnlyRuller(string s1, string s2);
public delegate void RullerCoronation(OnlyRuller d);

class Foo {
  private Foo();
  public Foo(RullerCoronation followMyOrders) {
     followMyOrders(SetMe);
  }

  private SetMe(string whatToSet, string whitWhatValue) {
     //lot of unclear but private code
  }
}

Quindi, in classe che crea questa proprietà si ha accesso al metodo SetMe, ma è ancora privato in modo tranne che per creatore Foo sembra unmutable.

Ancora per qualche cosa più grande di alcune proprietà questo sarà probabilmente presto divenne super-casino - è per questo che ho sempre preferito altri modi di incapsulamento. Tuttavia, se è super importante per voi di non permettere al cliente di cambiare Foo, che questo è un alternativa.

Tuttavia, come ho già detto, questa è solo teoria.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top