2016-08-01 1 views
-1

Ich denke, das ist eine häufig gestellte Frage und ich glaube, dass ich die Grundlagen gut behandelt habe, wie in wie Objekte, Strukturen oder Werttypen zwischen Funktionen übergeben werden. Nehmen wir an, ich die folgenden Klassen:Wie man eine Mutable nicht von einer Methode zurückgibt und Klasseninterna aussetzt

public class User{ 
    public int xCoor{get;set} 
    public int yCoor{get;set} 
} 

public class UserManager{ 
    Dictionary<int,User> userMapping = new Dictionary<int,User>(); 
    public void AddUser(User user){//stuff} 
    public void RemoveUser(User user){//stuff} 
    public IEnumerable<User> GetAllUsers(){return userMapping.Values;} 
} 

public class UserConsumer{ 
    private UserManager; 
    public void GetUserCoordinates(){ 
     var listThatExposesInternals = UserManager.GetAllUsers().ToList(); 
     listThatExposesInternals.Foreach(user => user.xCoor = user.yCoor = 0); 
    // Now that I altered the internals of the class, it is messed up 
    // I only want the consumer to be able read 
    } 
} 

Wie kann ich sicherstellen, dass die Interna der Benutzerklasse intakt bleibt. Ich bin mir bewusst, dass es für dieses Problem auch angebracht ist, xCoor und yCoor und nicht die gesamte Benutzerklasse offenzulegen, aber meistens stehe ich vor dem Problem, dass ich den Verweis auf die Klasse zurückgeben muss. Ich schätze jede Beratung.

+0

Wenn Sie nicht möchten, dass Personen ein Objekt mutieren können, machen Sie das Objekt nicht veränderbar ... – Servy

+0

Fragen Sie sich selbst: Wer sollte 'User's' xCoor' und '' ändern können yCoor'? – Andrew

+0

@Servy, vielen Dank für Ihre Antwort. Die Sache ist manchmal, dass ich benötige, dass Benutzerklasse in irgendeinem Teil des Rahmens mutiert wird und in anderen Teilen muss sie unveränderlich sein und nur verwendet werden, um ihre Werte zu betrachten. –

Antwort

1

Es gibt eine Reihe von Ansätzen, die Sie ergreifen könnten. Wenn es keine Situation gibt, in der xCoor und geändert werden sollten, nachdem User instanziiert wurde, könnten Sie die Werte in seinem Konstruktor anfordern und den Setter private sicherstellen, dass sie nicht außerhalb dieser Klasse geändert werden können.

public class User 
{ 

    public User(int xCoor, int yCoor) 
    { 
     this.xCoor = xCoor; 
     this.yCoor = yCoor; 
    } 

    public int xCoor{get; private set;} 
    public int yCoor{get; private set;} 
} 

Wenn Sie User brauchen wandelbar zu sein, aber die Änderung der Eigenschaften unter bestimmten Umständen entmutigen wollen, können Sie eine Schnittstelle für User implementieren schaffen, dass nur Getter auf diese Eigenschaften hat. Personen können jedoch weiterhin ihre IReadableUser als User und diese Eigenschaften zugreifen.

Eine weitere Option wäre das Erstellen einer neuen Klasse, die ein User-Objekt umschließt und nur Getter verfügbar macht, die die Eigenschaften von einer tatsächlichen User-Instanz lesen, aber nicht dieselben Eigenschaften festlegen können. Diese Option könnte mit der oben beschriebenen Methode IReadableUser kombiniert werden, um die Implementierungsdetails des zurückgegebenen Objekts auszublenden und gleichzeitig zu verhindern, dass Personen das Objekt an User übergeben, um seine Werte zu ändern.

public interface IReadOnlyUser 
{ 
    int xCoor {get;} 
    int yCoor {get;} 
} 

internal class ReadOnlyUser : IReadOnlyUser 
{ 
    private readonly User user; 

    public ReadOnlyUser(User user) 
    { 
     this.user = user; 
    } 

    public int xCoor{get { return this.user.xCoor; }} 
    public int yCoor{get { return this.user.yCoor; }} 
} 

public IEnumerable<IReadOnlyUser> GetAllUsers() 
{ 
    return userMapping.Values 
     .Select(u => (IReadOnlyUser) new ReadOnlyUser(u)); 
} 

Doch wäre eine weitere Option zu sein erlauben den Benutzern, die Werte Sie zurückkommen, mutieren, aber sicher, werden diese Werte Werte kopiert, so dass die nächste Mal, wenn jemand für die Liste der Benutzer fragt sie Instanzen, die haven sehen wurde nicht geändert.

public IEnumerable<User> GetAllUsers() 
{ 
    return userMapping.Values 
     .Select(u => new User { xCoor = u.xCoor, yCoor = u.yCoor }); 
} 

Wenn Sie mehr referenzbasierte Werte in Ihrer User Klasse haben, Ihre Select Aussage müßte neue Instanzen von denen, wie auch schaffen. Wenn das mühsam wird, sollten Sie in Betracht ziehen, jeder Klasse einen Kopierkonstruktor zu geben, um sicherzustellen, dass die Verantwortung für das Kopieren von Werten in den Teil des Codes konsolidiert wird, in dem sie am wahrscheinlichsten bemerkt und behoben wird, wenn sich Dinge ändern.

+0

Vielen Dank für Ihre Antwort. Ich habe über das Interface, IReadableUser, nachgedacht, aber wie du bereits erwähnt hast, kann es leicht in den Benutzer hineingeworfen werden und du kannst wieder auf Internes zugreifen. Dies wird besonders problematisch, wenn die Use-Klasse einige Eigenschaften als Objekte hat. –

+0

@ EmrahSüngü: Sie können eine Wrapper-Klasse verwenden, um dagegen zu schützen. Die Wrapperklasse kann entweder direkt verwendet werden oder eine Schnittstelle implementieren, wie in meinem aktualisierten Codebeispiel gezeigt. – StriplingWarrior

+0

Danke für eine sehr detaillierte Erklärung. Was ist, wenn eine der Eigenschaften, die ich darstellen möchte, kein primitiver Typ ist (in diesem Fall int), sondern ein anderes Objekt (zum Beispiel Address-Klasse mit einigen anderen Untereigenschaften). Wie schlägst du vor, dass ich dieses Problem umgehen sollte? –

0

Ich entschuldige mich dafür, dies als eine Antwort zu markieren. Ich habe meinen vorherigen Account verloren und habe nicht genug Reputation, um dies als Kommentar zu posten (noch nicht).

Eine Möglichkeit, dies zu tun, besteht darin, alle Eigenschaften readOnly zu setzen und die Werte über den Konstruktor zu setzen. Ich bin mir jedoch nicht sicher, ob das für Sie funktioniert. Wenn das Verhalten der Klasse den Zustand des Objekts ändert, kann dies einen Schraubenschlüssel in das Rad werfen.

Über die Liste, wenn die Liste eine Kopie des Objekts zurückgibt (d. H. Die Kopie der Liste zurückgibt), dann sollte das gut sein. Wenn Sie die einzelnen Objekte wollen nicht wandelbar sein, haben Sie 2 Möglichkeiten

  1. Make Kopie einzelner Datencontainer (Objekte) statt Kopieren nur die Referenzen (was dieser tut)
  2. die Datencontainer Erzwingt nicht veränderbar sein, indem die Eigenschaften nur als "get" verfügbar gemacht werden und die Setter als "private" festgelegt werden.

Hoffe, dass hilft.

0

Vielleicht die Sätze zu entfernen oder sie privat machen:

public class User{ 
    public int xCoor{get;} 
    public int yCoor{get;} 
} 

oder

public class User{ 
    public int xCoor{get; private set;} 
    public int yCoor{get; private set;} 
} 
+0

Sie müssen auch einen Konstruktor angeben, oder sie haben keine Möglichkeit, die Eigenschaften überhaupt festzulegen. –

0

Es gibt ein paar Möglichkeiten. Ich vermute, dass Sie nur das "Set" aus jedem Satz entfernen müssen.

könnten Sie haben:

public class User 
{ 
    protected int _xCoor = 0; 
    protected int _yCoor = 0; 

    public int xCoor 
    { 
     get { return _xCoor; } 
    } 

    public int yCoor 
    { 
     get { return _yCoor; } 
    } 
} 

In meinem Beispiel Fall wird, um die Werte von _xCoor und _yCoor zu ändern, würden Sie eine Klasse erstellt haben, die von der Benutzerklasse erbt.

+0

Wenn 'code User' Klasse einige Eigenschaften als Objekte hat, dann kann ich das zugrunde liegende Objekt ändern, richtig? –

+0

ja, du hast recht; Wenn Sie jedoch eine Klasse erstellen, die von Benutzer erbt, können Sie die Funktionen platzieren, die die Werte dort ändern. Dann haben Sie die Wahl, die Benutzerklasse (nichts kann gesetzt werden) oder die Klasse, die von einem Benutzer mit öffentlichen Funktionen (kann Dinge setzen) erben. – Nez

Verwandte Themen