c# IEnumerable属性.向集合中添加项是调用赋值器还是仅调用赋值器?

本文关键字:赋值 调用 属性 IEnumerable 集合 添加 | 更新日期: 2023-09-27 18:16:55

假设我有

class A
{
 public List<B> LiProperty
 {
    get;
    set { //will I get called when someone calls A::LiProperty.Add()? }
 }
}
然后

A a = new A();
a.LiProperty.Add(new B());

会被调用吗?

我的直觉告诉我get是返回一个指向列表的指针,所以add方法是直接在对象上调用的,但是c#有时会对不可变类型做一些奇怪的事情。有人确定知道答案吗?

c# IEnumerable属性.向集合中添加项是调用赋值器还是仅调用赋值器?

你的直觉是对的。在上面的代码片段中,调用get访问器,并在返回的List上调用Add方法。

改变属性就是改变它指向另一个不同的List<T>。将对象添加到列表是而不是更改 List<T>属性指向的。代码不会调用setter,除非您写a.LiProperty = new List<T>();

No.

像其他属性一样,setter只在写入LiProperty = something时运行。
这将会用一个新的列表实例替换掉这个列表(除非你在setter中做了一些奇怪的事情)

一般来说,集合属性应该是只读的。

在。net中,类型T的属性只不过是指向一对方法的链接,其中一个具有签名:T get_method( [optional arguments, in case of indexed property] );,另一个具有签名:void set_method(T value, [optional arguments, in case of indexed property] );。在c#中,只有当属性位于赋值操作符的左半部分时才调用set_method;否则,调用get_method(*)。请注意,在。net中,属性通常无法知道它返回的结果被做了什么,也无法在调用者处理完返回的对象时得到通知。

(*)在vb.net中,在某些情况下允许编写看起来像通过引用传递属性的代码。在这种情况下,编译器的实际行为是将属性"获取"为一个temp,通过引用传递该temp,调用该函数,然后将属性"设置"为函数在temp中留下的值。

从代码的角度来看,一个更有用的模式是使用一个方法调用指定的委托,并将支持属性的字段作为'ref'参数。这样就可以执行如下操作:

<>之前MyShape。ActOnBounds((ref矩形边界)=>{边界。X -= bounds.width/2;范围之内。});之前

,并让MyShape对"bounds"的更改值进行操作,而无需复制或创建任何多余的Rectangle实例。不幸的是,虽然这样的转换可能相当高效(特别是如果ActOnBounds可以是一个静态方法,接受一个或多个泛型类型参数,并使用这些类型的ref参数调用提供的例程),但客户端代码有点难看。

顺便说一下,即使。net没有结构类型,上面的模式对于基本类型或引用属性仍然是有用的(比get- manipulation -set更好),因为它允许使用像Interlocked这样的东西。增量和互锁。比较交换。

如果您想以一种可以发现它何时更新的方式公开列表,一种方法是定义一个不可变的结构类型,该类型保存对对象的引用,并通过将所有IList方法重定向到对象的私有方法来实现IList(这将反过来在列表上执行并适当地对它们进行操作)。请注意,尽管是不可变结构体,但新类型将使用引用语义(这意味着复制该结构体并在其上调用"Add"方法将导致新项在原始和复制的结构体中都可见)。

这样做可以避免在直接对返回的列表属性执行方法的情况下(例如myThing.LiProperty.Add("George");)和在使用返回类型创建变量的情况下(例如var myList = myThing.LiProperty;)装箱。如果返回的对象被赋值给一个接口类型(例如IList)的变量或传递给参数时,就会发生装箱。

相关文章: