我有一个类 LoginManager
,它有一个私有(private)字段 currentUser
和一个公共(public)属性 CurrentUser
以防止自己意外更改CurrentUser
来自 LoginManager
类之外。
CurrentUser
是 { get; }
但我仍然可以更改底层 currentUser
中的属性,这是私有(private)的。
例如。
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
LoginManager.cs
public class LoginManager
{
private User? currentUser { get; set; }
private readonly ApplicationDbContext dbContext;
public event EventHandler CurrentUserChanged;
public User? CurrentUser
{
get { return currentUser; }
}
//...
}
用户.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; }
}
我想要它,这样我就不能从 LoginManager
类之外更改 CurrentUser
。有人能指出我正确的方向吗?
最佳答案
首先要了解的是,缺少的 set
访问器只会阻止您更改 CurrentUser
属性本身的值。也就是说,它会阻止您将其更改为另一个 User
对象(或 null)。但是,这些规则仅适用于属性本身,而不适用于其中的任何内容。属性的只读性质不是递归的。
如果您想防止更改 CurrentUser
中包含的 User
对象内的属性,则需要有一个不可变的 User
对象某种类型的,或者您必须防止直接访问该对象并提供允许从用户检索某些数据而不能更改它的辅助方法。因此,这两个选项是隐藏直接访问或使 User
对象本身不可变。
第一个选项是隐藏对 User
对象的访问:
public User? CurrentUser { get; }
public string GetCurrentUserName() {
return CurrentUser?.Name;
}
public string GetCurrentUserUsername() {
return CurrentUser?.UserName;
}
以上遵循所谓的得墨忒耳法则,但我不认为它经常使用,因为它不符合人体工程学并且会膨胀类。仅当当前用户的许多详细信息对于使用 LoginManager
进行编码并不重要并且此类代码不需要能够绕过 User
对象。
第二个选项有一些子选项。您可以创建一个只公开只读属性的 IReadOnlyUser
接口(interface):
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
}
此外,您可以像在其他答案中一样,将 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
在我看来,没有一个特定的正确答案。这取决于你想做什么。
关于c# - 为什么我可以设置私有(private)设置属性的属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70726321/