如何不 return 方法中的可变变量并公开 class 内部结构
How not to return a mutable from a method and exposing class internals
我认为这是一个常见问题,我相信我已经很好地涵盖了基础知识,例如对象、结构或值类型如何在函数之间传递。让我们假设我有以下 classes:
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
}
}
如何确保用户 class 的内部结构保持完整。我知道对于这个问题,公开 xCoor 和 yCoor 而不是整个用户 class 也是合适的,但大多数时候我遇到的问题(需要?) return 对 class。我感谢任何建议。
很抱歉将此标记为答案。失去了我以前的帐户并且没有足够的声誉 post 作为评论(还没有)。
一种方法是将所有属性设置为只读并通过构造函数设置值。但是,我不确定这是否适合您。如果 class 的行为修改了对象的状态,那么这可能会造成麻烦。
关于List,如果listreturns对象的副本(即returnsList的副本)那应该不错。如果您希望单个对象不可变,您有 2 个选项
- 制作单个数据容器(对象)的副本,而不是仅仅复制引用(这一个所做的)
- 通过将属性公开为 get only 并将 setter 设置为私有来强制数据容器不可变。
希望对您有所帮助。
也许删除集或将它们设为私有:
public class User{
public int xCoor{get;}
public int yCoor{get;}
}
或
public class User{
public int xCoor{get; private set;}
public int yCoor{get; private set;}
}
您可以采用多种方法。如果 xCoor
和 yCoor
在实例化 User
之后永远不会发生更改的情况,您可以在其构造函数中要求值并使 setter private
以确保无法在此 class.
之外更改它们
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;}
}
如果您确实需要 User
是可变的,但又不想在某些情况下更改属性,您可以为 User
创建一个接口来实现,该接口仅对这些属性具有 getter。但是,人们仍然可以将他们的 IReadableUser
转换为 User
并访问这些属性。
另一种选择是创建一个新的 class 来包装 User
对象,并且只公开从实际 User
实例读取属性但不能设置这些属性的 getter相同的属性。此选项可以与上面的 IReadableUser
方法结合使用,以隐藏 returned 对象的实现细节,同时仍然防止人们将对象强制转换为 User
以更改其值。
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));
}
另一种选择是 允许 用户更改您 return 的值,但要确保这些值是复制值,以便下次有人要求他们看到的实例未更改的用户列表。
public IEnumerable<User> GetAllUsers()
{
return userMapping.Values
.Select(u => new User { xCoor = u.xCoor, yCoor = u.yCoor });
}
如果您的 User
class 中有更多基于引用的值,您的 Select
语句也需要创建这些值的新实例。如果这变得很麻烦,您可能会考虑为每个 class 提供一个复制构造函数,以确保将复制值的责任合并到代码的一部分,当事情发生变化时,它最有可能被注意到和修复。
有几种方法。我怀疑您只需要从每个 set/get 中删除 "set"。
你可以:
public class User
{
protected int _xCoor = 0;
protected int _yCoor = 0;
public int xCoor
{
get { return _xCoor; }
}
public int yCoor
{
get { return _yCoor; }
}
}
在我的示例中,为了更改 _xCoor 和 _yCoor 的值,您必须创建一个继承自用户 class 的 class。
我认为这是一个常见问题,我相信我已经很好地涵盖了基础知识,例如对象、结构或值类型如何在函数之间传递。让我们假设我有以下 classes:
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
}
}
如何确保用户 class 的内部结构保持完整。我知道对于这个问题,公开 xCoor 和 yCoor 而不是整个用户 class 也是合适的,但大多数时候我遇到的问题(需要?) return 对 class。我感谢任何建议。
很抱歉将此标记为答案。失去了我以前的帐户并且没有足够的声誉 post 作为评论(还没有)。
一种方法是将所有属性设置为只读并通过构造函数设置值。但是,我不确定这是否适合您。如果 class 的行为修改了对象的状态,那么这可能会造成麻烦。
关于List,如果listreturns对象的副本(即returnsList的副本)那应该不错。如果您希望单个对象不可变,您有 2 个选项
- 制作单个数据容器(对象)的副本,而不是仅仅复制引用(这一个所做的)
- 通过将属性公开为 get only 并将 setter 设置为私有来强制数据容器不可变。
希望对您有所帮助。
也许删除集或将它们设为私有:
public class User{
public int xCoor{get;}
public int yCoor{get;}
}
或
public class User{
public int xCoor{get; private set;}
public int yCoor{get; private set;}
}
您可以采用多种方法。如果 xCoor
和 yCoor
在实例化 User
之后永远不会发生更改的情况,您可以在其构造函数中要求值并使 setter private
以确保无法在此 class.
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;}
}
如果您确实需要 User
是可变的,但又不想在某些情况下更改属性,您可以为 User
创建一个接口来实现,该接口仅对这些属性具有 getter。但是,人们仍然可以将他们的 IReadableUser
转换为 User
并访问这些属性。
另一种选择是创建一个新的 class 来包装 User
对象,并且只公开从实际 User
实例读取属性但不能设置这些属性的 getter相同的属性。此选项可以与上面的 IReadableUser
方法结合使用,以隐藏 returned 对象的实现细节,同时仍然防止人们将对象强制转换为 User
以更改其值。
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));
}
另一种选择是 允许 用户更改您 return 的值,但要确保这些值是复制值,以便下次有人要求他们看到的实例未更改的用户列表。
public IEnumerable<User> GetAllUsers()
{
return userMapping.Values
.Select(u => new User { xCoor = u.xCoor, yCoor = u.yCoor });
}
如果您的 User
class 中有更多基于引用的值,您的 Select
语句也需要创建这些值的新实例。如果这变得很麻烦,您可能会考虑为每个 class 提供一个复制构造函数,以确保将复制值的责任合并到代码的一部分,当事情发生变化时,它最有可能被注意到和修复。
有几种方法。我怀疑您只需要从每个 set/get 中删除 "set"。
你可以:
public class User
{
protected int _xCoor = 0;
protected int _yCoor = 0;
public int xCoor
{
get { return _xCoor; }
}
public int yCoor
{
get { return _yCoor; }
}
}
在我的示例中,为了更改 _xCoor 和 _yCoor 的值,您必须创建一个继承自用户 class 的 class。