C#如何在get;set;中将变量返回为只读?

36

我发誓我曾经看过一个例子,但是找了一段时间并没有找到。

我有一个包含对象引用的类,需要为其编写GET方法。我的问题是,我不希望任何人都能够做出更改,即我希望他们获得只读版本的对象(请注意,我需要能够从我的类内部进行更改)。

谢谢


1
我很惊讶有多少人误解了这个问题。Jon Skeet和Anton Gogolev回答得对。 - Chris Cudmore
1
我并不认为我有误解,但仍然被踩了 - 哦,好吧。 - ChrisF
@ChrisCudmore,我认为Anton Gogolev在这里不是主题。而对于Jon来说,他总是做正确的事情 :) - tymtam
7个回答

54

不,没有办法做到这一点。例如,如果您返回一个List<string>(并且它不是不可变的),那么调用者能够添加条目。

解决此问题的常规方法是返回一个不可变的包装器,例如ReadOnlyCollection<T>

对于其他可变类型,您可能需要在返回之前克隆该值。

请注意,仅返回不可变接口视图(例如返回IEnumerable<T>而不是List<T>)无法阻止调用者将其强制转换回可变类型并进行变异。

编辑:请注意,除此之外,这种关注点是不可变类型使代码更易于推理的原因之一 :)


3
你先说这件事情做不到,然后你又提出了克隆的解决方案。此外,即使在 c# 中克隆是一个新的想法,但在 c++ 中已经存在了很长时间。而且这并不严格意义上的克隆——它是创建内部数据的副本并返回此副本,而不是原始数据——该副本不必与原始数据具有相同的类型。 - tymtam

6

返回一个精简接口的引用:

 interface IFoo
   string Bar { get; }

 class ClassWithGet
   public IFoo GetFoo(...);

2
最好包括一个privateinternal实现的IFoo,这样它就无法被强制转换回来 ;) - jerryjvl
哦,如果接口是内部的或私有的,您将无法返回具有不可访问类型的公共成员。这将无法编译:“不一致的可访问性:字段类型...比字段...不可访问”。 - Julien N

4
如果对象不太复杂/庞大,那么可以编写一个包装器来处理它。
例如:
class A {
    public string strField = 'string';
    public int intField = 10;
}

class AWrapper {
    private A _aObj;

    public AWrapper(A aobj) {
      _aObj = A;
    }

    public string strField {
         get {
            return _aObj.strField;
         }
    }

    public int intField {
         get {
            return _aObj.intField;
         }
    }
}

现在,您只需要给客户端代码一个AWrapper类的实例,以便他们只能使用您允许他们看到的内容。
如果您的基类不是固定的,则可能会变得有些复杂,并且可能无法很好地扩展,但对于大多数简单情况,这可能就足够了。我认为这被称为外观模式(但请不要引用我 =))。

这就是我一直在寻找的解决方案。我还应该补充一点,即使你想要包装的对象很复杂,也不意味着你必须实际包装每个方法/函数。在我的情况下,我只想包装一个非常复杂的对象,并展示特定的方法。例如,您的复杂对象可以有20个方法,但您包装的对象只能访问其中的5个。 - Pablo Alexis Domínguez Grau

3

这是不可能的。对于引用类型,获取和设置访问器会获取并设置对象的引用。您可以使用私有(或内部)setter来防止引用的更改,但是如果通过getter公开了对象本身,则无法防止对象本身的更改。


是的,这是可能的。您可以返回数据的副本。 - tymtam

2

您的问题看起来是在寻找以下信息:

public PropertyName { get; private set; }

然而,考虑到迄今为止的答案,我不确定我是否正确解释了你的问题。此外,我又有什么资格质疑Jon Skeet呢? :)


返回一个只读引用,因为它不能被赋值,但是你仍然可以编辑该对象,因为它的属性不是只读的。 - DCShannon
这对我很有帮助!这是一个聪明的方法,可以使某些内容公开但不可写。 - Shadoninja

0

我同意 ReadOnlyCollection。

看看我的简单代码:

 private List<Device> _devices;
public readonly System.Collections.ObjectModel.ReadOnlyCollection<Device> Devices 
{
 get
 { 
return (_devices.AsReadOnly());
 } 

}

ReadOnlyCollection 没有 Add 方法,因此用户无法向其中添加属性。但是不能保证用户无法通过调用其方法修改对象...


0

我以某种方式遇到了这个问题。我有一个CategoryViewModel类,其中有一个我想要私有只读的属性Category:

public CategoryViewModel
{
    private Category { get; }

}

实际上,我希望将其导出为只读到其他类。但是我无法做到这样的事情。 在我的情况下(也许会帮助其他人),我想将其添加到存储库中。我找到的唯一方法是使用一个具有存储库作为参数1和Action作为参数2的函数:
public void ApplyAction(ICategoryRepository repo, Action<ICategoryRepository, Category> action)
{
    action(repo, Category);
}

就像这样,从其他地方,我可以做这样的事情:

 categoryViewModel.ApplyAction(_repository, (r, c) => r.MarkForInsertOrUpdate(c));

这可以帮助其他人仅在某些情况下暴露其属性并对其进行管理。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接