c# - 为什么我可以设置私有(private)设置属性的属性?

标签 c# .net .net-core

我有一个类 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/

相关文章:

c# - XML 序列化不会写出延迟评估的属性

.net - SOAP,在标签 <xx :tag> 中使用前缀

c# - Silverlight 从 'dumb' 服务器按需加载引用数据

.net-core - EF Core Migration Add - 使用意外的 DropForeignKey、DropUniqueConstraint、DropColumn 语句创建迁移

sql-server - .NET Core - 迁移 Fluent API HasColumnType

angular - .NET Core Angular 7 服务器端渲染

c# - 如何使用 INotifyDataErrorInfo 接口(interface)验证 Observable 集合

c# - 在 c# xaml 中创建一个数学图

c# - 根据C#中的日期从Xml中过滤数据

c# - 检查滚动条是否在数据 GridView 中可见