我知道如何使用属性,我理解他们隐含地调用底层get
和set
访问器,具体取决于我们是写入还是读取属性。
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访问器返回的对象的成员(假设支持字段是引用类型),那会不会更有效?
谢谢
答案 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)
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字段或属性的访问级别的需求。