我有一个类LoginManager
,它有一个私有字段currentUser
和一个公共属性CurrentUser
,不允许自己意外地CurrentUser
从LoginManager
类外部更改其值。
CurrentUser
只是{ get; }
,但我仍然可以更改底层currentUser
私有的属性。
例如。
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()); // true
Run Code Online (Sandbox Code Playgroud)
登录管理器.cs
public class LoginManager
{
private User? currentUser { get; set; }
private readonly ApplicationDbContext dbContext;
public event EventHandler CurrentUserChanged;
public User? CurrentUser
{
get { return currentUser; }
}
//...
}
Run Code Online (Sandbox Code Playgroud)
用户.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; }
}
Run Code Online (Sandbox Code Playgroud)
我想要它,这样我就无法CurrentUser
在课堂之外进行更改LoginManager
。有人能指出我正确的方向吗?
首先要了解的是,缺少set
访问器只会阻止您更改属性本身的值CurrentUser
。也就是说,它阻止您将其更改为另一个User
对象(或 null)。然而,这些规则仅适用于财产本身,而不适用于财产内部的任何东西。属性的只读性质不是递归的。
User
如果您想防止更改中包含的对象内部的属性CurrentUser
,则需要有User
某种类型的不可变对象,或者必须防止直接访问该对象并提供帮助程序方法,允许从用户检索某些数据而无需能够改变它。因此,这两个选项是隐藏直接访问或使User
对象本身以某种方式不可变。
第一个选项是隐藏对User
对象的访问:
public User? CurrentUser { get; }
public string GetCurrentUserName() {
return CurrentUser?.Name;
}
public string GetCurrentUserUsername() {
return CurrentUser?.UserName;
}
Run Code Online (Sandbox Code Playgroud)
上面的内容遵循所谓的德米特法则,但我不认为它经常被使用,因为它不符合人体工程学并且会导致类膨胀。仅当当前用户的许多详细信息对于使用 的代码并不重要LoginManager
并且此类代码不需要能够传递User
对象时才采用此路线。
第二个选项有一些子选项。您可以创建一个IReadOnlyUser
仅公开只读属性的接口:
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
}
Run Code Online (Sandbox Code Playgroud)
此外,您可以像其他答案一样,将User
类中的字段设置为只读,无论是使用readonly
字段还是仅具有get
访问器的属性。这取决于您是否认为在某些情况下您应该能够修改User
对象的属性,但不能在LoginManager
.
最后,如果您不控制该类User
,则可以创建一个包装对象User
但仅公开 getter 的外观类:
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 Name
Run Code Online (Sandbox Code Playgroud)
在我看来,没有特定的唯一正确答案。这取决于你想做什么。
归档时间: |
|
查看次数: |
926 次 |
最近记录: |