这是否会破坏拥有只读属性的整个目的?

时间:2010-06-16 19:14:53

标签: c# properties

我知道如何使用属性,我理解他们隐含地调用底层getset访问器,具体取决于我们是写入还是读取属性。

static void Main(string[] args)
{
    A a = new A();
    (a.b).i = 100;

}

class A 
{
    private B _b = new B();
    public B b
    {
        get { return _b; }
    }
}
class B  
{
    public int i;
}

代码(a.b).i = 100;基本上做的是第一个属性的get访问者返回对象_b的引用,一旦我们有了这个引用,我们就可以访问{{1}成员并改变他们的价值观。

因此,在我们的示例中,具有只读属性仅会阻止外部代码更改引用变量_b’s的值,但它不会阻止外部代码访问_b成员。

因此,似乎属性只能检测我们是否正在尝试读取或写入位于堆栈上的变量(在我们的示例中为变量_b’s),而它无法检测我们是否正在尝试也写入堆栈上的变量(假设此变量是引用类型)指向的对象的成员。

a)但这不是打破了只读属性的全部目的吗?如果属性能够检测我们是否正在尝试访问由get访问器返回的对象的成员(假设支持字段是引用类型),那会不会更有效?

谢谢

13 个答案:

答案 0 :(得分:27)

不变性不是传递性的;你不能指望可变对象成为不可变的访问器是不可变的。

答案 1 :(得分:19)

您的参考是只读的,而不是您的对象。

答案 2 :(得分:13)

想象一下这样的课程:

public class A
{
    private List<int> _myList<int> = new List<int>();
    public List<int> MyList { get { return _myList; } }
}

现在,该类用户可以添加,删除和访问列表中的项目,但他们无法替换列表本身。这个很重要。它允许你在类中做一些事情,例如假设_myList成员永远不为null。

采用更通用的方式,这个范例允许您在类中定义一个接口,以便用户可以使用您公开的属性中的类型,但是它们不能只从您下面交换复杂类型的实例。

答案 3 :(得分:9)

  1. 不,它不会破坏只读属性的目的。
  2. 可以使用不允许用户更改基础数据的只读属性。例如,即使基础类型为System.Collections.ObjectModel.ReadOnlyCollection,您也可以让属性返回List。当然,这不会阻止用户更改集合中项的属性。

答案 4 :(得分:6)

当然你可以访问B.i;它是public。您认为_b是私有的,所有方法在通过A获取时都应该是私有的?在这种情况下,它是无用的,因为你无法使用B做任何事情。

答案 5 :(得分:5)

你问:

  

这不是打败了整个目的   有只读属性?

但请注意:您的B.i成员是公共字段

我问,那么:拥有公共字段的目的是什么?只有当希望代码的用户能够更改该字段的值时才有意义。如果您想要它,它应该是私有字段,或者(如果您想提供读取但不是写访问权限)具有私有set访问者的属性。

所以这是你的答案。 private B _b在您发布的代码中很有用(_b无法在外部设置为新内容),就像public int i提供目的一样同样地好(i 可以在外部进行更改)。

答案 6 :(得分:4)

引用不变性是一种流行的功能请求。太糟糕了,它非常不符合CLS。很少有语言有这个概念,我只知道C ++(但不要太多)。

CLR需要强制执行的关键问题。 C ++不需要在运行时强制执行此操作,只需要C ++编译器即可确保遵守const契约。它完全没有语言互操作的支持,除了像COM这样的插件之外。

这不会在.NET中出现,在声明引用不可变时没有什么意义,并且当另一种语言可以踩踏它时,编译器会验证它,因为它没有表达不变性的语法。我想我们有一天会得到它,而不是真的很快。

答案 7 :(得分:2)

作为一个小问题,你不必写(a.b).i = 100; ,只需

a.b.i = 100;

回到你的问题,我不认为它违背了目的。您仍然无法执行以下操作:

a.b = new B();

因为没有公共集()。如果你想让B类的成员i只读,你可以做同样的事情,就像你对A类的成员_b做一样私有并提供一个公共的get(),而不是set()。从头到尾,做你提出的建议可能会导致许多意想不到的一致性(我确信语言设计师并没有忽视这一点)。

答案 8 :(得分:2)

完全依赖于这种情况,但只读访问可变对象是一种常用的设计。在许多情况下,您只是想确保对象本身保持不变。

有些类(比如Java中的String对象,我也相信C#)是完全不可变的,而其他类只是部分可变的。考虑一个ActiveRecord样式的对象,其中大多数字段是可变的,但ID是不可变的。如果您的类在只读属性中包含ActiveRecord,则外部类不能将其交换为不同的ActiveRecord对象,从而更改ID,这可能会破坏您的类中的假设。

答案 9 :(得分:2)

我不同意。您的属性适用于B类,而不适用于B类成员。这意味着您无法将新对象分配给b。这并不意味着B的公众成员突然变得私密。

答案 10 :(得分:2)

readonly适用于类属性,而不是属性引用的对象。它使您无法编写a.b = new B();,这就是它所做的一切。一旦您获得对象的引用,它就不会限制您对该对象的操作。我认为你发现的是readonly在应用于值类型或不可变类类型时最有意义。

答案 11 :(得分:2)

另一个用例:

interface INamedPerson
{
    String Name { get; }
}

class Bob : INamedPerson
{
    public String Name { get; set; }
}

class Office
{
    // initialisation code....

    public INamedPerson TheBoss { get; }

    public IEnumerable<INamedPerson> Minions { get; }
}

现在,如果您有一个Office实例,只要您不使用强制转换作弊,您就拥有对每个人姓名的只读权限,但不能更改其中任何一个。

答案 12 :(得分:1)

阿。封装实例化的类继承了包含类的访问级别。将B类公开为A型公共财产是公开的,所以它应该可以从外面以“A.b”公开的方式进入。

A.b返回私人可访问类型B的引用,但是类型B具有可公开访问的字段i。我的理解是你可以设置B的i字段,但不能在外部设置A的b属性。 A的B类型属性是readonly但是对类型B的引用没有定义对其字段的相同只读访问。

我确信您可以修改B类的定义,以满足您对B字段或属性的访问级别的需求。