Tengo una clase LoginManager que tiene un campo privado currentUser y una propiedad pública CurrentUser para no permitirme cambiar accidentalmente el valor de CurrentUser desde fuera de la clase LoginManager .
CurrentUser es { get; } solo, pero aún puedo cambiar las propiedades en el currentUser subyacente que es privado.
p.ej.
Console.WriteLine(loginManager.CurrentUser.ClockedIn.ToString()); // true loginManager.CurrentUser.ClockedIn = false; Console.WriteLine(loginManager.CurrentUser.ClockedIn.ToString()); // false loginManager.CurrentUser.ClockedIn = true; Console.WriteLine(loginManager.CurrentUser.ClockedIn.ToString()); // trueLoginManager.cs
public class LoginManager { private User? currentUser { get; set; } private readonly ApplicationDbContext dbContext; public event EventHandler CurrentUserChanged; public User? CurrentUser { get { return currentUser; } } //... }Usuario.cs
public class User { public Guid Id { get; set; } public string Name { get; set; } public string Username { get; set; } public string Password { get; set; } public bool ClockedIn { get; set; } } Lo quiero para que no pueda cambiar CurrentUser desde fuera de la clase LoginManager . ¿Alguien podría indicarme la dirección correcta?
Creo que veo el problema, las instancias privadas de CurrentUser get y set propiedades que están expuestas fuera de la clase, ya que esas variables miembro tienen un alcance public .
¿Por qué no hacer que las propiedades del User sean private ?
public class User { private Guid Id { get; set; } private string Name { get; set; } private string Username { get; set; } private string Password { get; set; } private bool ClockedIn { get; set; } } O si necesita get el private set marcas de propiedades:
public class User { public Guid Id { get; private set; } public string Name { get; private set; } public string Username { get; private set; } public string Password { get; private set; } public bool ClockedIn { get; private set; } } O hágalos explícitamente de readonly para que solo puedan inicializarse en el Constructor :
public class User { public readonly Guid Id { get; private set; } public readonly string Name { get; private set; } public readonly string Username { get; private set; } public readonly string Password { get; private set; } public readonly bool ClockedIn { get; private set; } public User(Guid Id, string Name, string Username, string Password, bool ClockedIn) { this.Id = Id; this.Name = NameName; this.Username = Username; this.Password = Password; this.ClockedIn = ClockedIn; } }Lo primero que debe comprender es que el descriptor de acceso set faltante solo le impide cambiar el valor de la propiedad CurrentUser en sí. Es decir, le impide cambiarlo a otro objeto User (o nulo). Sin embargo, estas reglas solo se aplican a la propiedad en sí, y no a nada dentro de ella. La naturaleza de solo lectura de las propiedades no es recursiva.
Si desea evitar cambiar las propiedades dentro del objeto User contenido en CurrentUser , deberá tener un objeto User inmutable de algún tipo, o debe evitar el acceso directo al objeto y proporcionar métodos auxiliares que permitan recuperar ciertos datos del usuario sin poder cambiarlo. Las dos opciones son, por lo tanto, ocultar el acceso directo o hacer que el objeto User sea de alguna manera inmutable.
La primera opción es ocultar el acceso al objeto User :
public User? CurrentUser { get; } public string GetCurrentUserName() { return CurrentUser?.Name; } public string GetCurrentUserUsername() { return CurrentUser?.UserName; } Lo anterior sigue la llamada Ley de Deméter, pero no veo que se use con demasiada frecuencia ya que no es ergonómico e infla las clases. Siga esta ruta solo si muchos de los detalles del usuario actual no son importantes para el código que usa LoginManager y si dicho código no necesita poder pasar el objeto User .
La segunda opción tiene algunas subopciones. Puede crear una interfaz IReadOnlyUser que solo exponga propiedades de solo lectura:
interface IReadOnlyUser { public string Name { get; } public string Username { get; } } public class User : IReadOnlyUser { // although these have setters, they cannot be access through variables of type IReadOnlyUser (see below) public string Name { get; set; } public string Username { get; set; } } public class LoginManager { // you can use a field here instead of a property since it is private private User currentUser; // returns an read-only view of the user, so write accessors are not available public IReadOnlyUser CurrentUser { get { return currentUser; } } } // usage var lm = new LoginManager(); Console.WriteLine(lm.CurrentUser.Name); // okay because IReadOnlyUser has a read accessor for Name lm.CurrentUser.Name = "foo"; // not allowed because IImutableUser does not expose a write accessor } Además, puede hacer lo mismo que en la otra respuesta y hacer que los campos en la clase de User sean de solo lectura, ya sea con campos de readonly o con propiedades que solo tienen accesos de get . Depende de si cree que hay casos en los que debería poder modificar las propiedades de los objetos User , pero no en el contexto de LoginManager .
Finalmente, si no controla la clase User , puede crear una clase de fachada que envuelva un objeto User pero solo exponga captadores:
public class ReadOnlyUser { private User user; public ReadOnlyUser(User user) { this.user = user; } public string Name { get { return user.Name; } } } // in LoginManager private ReadOnlyUser currentUser; public ReadOnlyUser CurrentUser { get { return currentUser; } set { currentUser = new ImmutableUser(value); } } // usage var lm = new LoginManager(); lm.CurrentUser = userFromSomewhereElse; Console.Log(lm.CurrentUser.Name); // okay because ReadOnlyUser exposes this property from the underlying User class lm.CurrentUser.Name = "foo"; // not allowed because ReadOnlyUser doesn't have a setter for NameEn mi opinión, no hay una única respuesta correcta en particular. Depende de lo que quieras hacer.