属性 - 按值或参考?
Posted
技术标签:
【中文标题】属性 - 按值或参考?【英文标题】:Properties - by value or reference? 【发布时间】:2011-07-13 14:00:43 【问题描述】:我有以下公开 Arraylist
的公共属性:
public ArrayList SpillageRiskDescriptions
get
return _SpillageRiskDescriptions;
set
_SpillageRiskDescriptions = value;
我在其他地方打电话
SpillageRiskDescriptions.Add("VENTILATE AREA");
SpillageRiskDescriptions.Add("DO NOT ALLOW SPILLAGE TO ENTER MAINS");
这些似乎是向私有 ArrayList _SpillageRiskDescriptions
添加元素(通过属性),而我预计这会导致问题。因此,我认为属性将 reference 返回到原始变量而不是通过 value 传递它是否正确?这是因为ArrayList
是引用类型吗? int
也会发生同样的情况(例如?)
【问题讨论】:
【参考方案1】:从技术上讲,它总是按价值计算,但您必须了解传递的内容。由于它是一种引用类型,因此您将引用传回(但按值)。
希望这是有道理的。你总是按值传回结果,但如果类型是引用,则按值传回引用,这意味着你可以更改对象,但不能更改它引用的对象。
【讨论】:
把它想象成C++,你可以传递一个指向一个对象的指针,你可以改变这个指针指向的东西,但是你不能让它指向别的东西,因为你有一个指针而不是原始指针。如果要更改它指向的对象,则必须将指针传递给指针(或对指针的引用)。这就是为什么在 C# 中如果要更改所引用的对象,则需要 ref 和 out 关键字。 优秀 - 这很有意义【参考方案2】:其他答案已正确回答了您的主要问题:引用类型的属性的值是对的引用引用类型的>实例。
更大的问题是“现在你该怎么办?”大概您确实不希望可以获得列表的代码能够更改它。
这里有几个选项。
首先,无论您做什么,我都建议您立即停止使用 ArrayList 并开始使用更现代、更安全的类型来存储事物列表。 List<string>
立即浮现在脑海中。
其次,属性的setter是否需要公开?您是否希望任何人都能够更改该列表?或者类型本身是否应该负责以某种方式更新它?我会考虑将 setter 设为私有。
第三,我们为“琐碎”属性提供了更紧凑的语法。你可能会说
public List<string> SpillageListDescriptions get; private set;
编译器会为你生成支持字段。
请注意,List<string>
仍然允许突变。
如果客户关心的是能够一次遍历列表中的一件事,那么您可以这样做:
private List<string> descriptions = whatever;
public IEnumerable<string> SpillageListDescriptions
get
if (descriptions == null) yield break;
foreach(var description in descriptions)
yield return description;
现在调用者不可能改变列表,因为你给他们的只是一个迭代器,而不是访问列表本身。
或者你可以这样做:
private List<string> descriptions = whatever;
public IList<string> SpillageListDescriptions
get
return new ReadOnlyCollection<string>(descriptions);
现在调用者有一个列表的只读视图,如果他们试图修改它会抛出异常。
【讨论】:
非常感谢您的反馈,但是其中大多数(短属性语法和带有公共 getter 的私有 setter)仅在框架的更高版本中可用 - 我使用的是 .NET2。我实际访问它的全部原因是我可以将 ArrayList 转换为 Listvar
替换为string
。【参考方案3】:
C# 通过引用返回对象。
只有不按值返回的东西是:
原始类型。 结构类型。 枚举。
【讨论】:
本质上是正确的,尽管从技术上讲,它通过值传递回对象引用。 c++ 指针也是按值传回的,但这是按引用的定义。传递的是对象的地址,而不是对象本身。【参考方案4】:与是否是属性本身无关;它是属性的数据类型。在这种情况下,您使用的是ArrayList
,它是一个通过引用传递的类。如果属性被键入为int
,它将按值传递。
【讨论】:
【参考方案5】:一个属性实际上是一对两个方法——一个setter和一个getter。它们按原样传递支持字段(或用作源/目标的任何值)的值。因此,对于引用类型,传递引用,而对于值类型,传递值。
// this is how the getter method looks like
public ArrayList get_SpillageRiskDescriptions()
return _SpillageRiskDescriptions;
// this is how the setter method looks like
public void set_SpillageRiskDescriptions(ArrayList value)
_SpillageRiskDescriptions = value;
请注意,通过将ArrayList
实例分配到您的属性中,您基本上替换支持字段指向的ArrayList
实例。
回答您的特定问题:
因此,我认为属性返回对原始变量的引用而不是通过值传递它是否正确?
不,通过使用 getter,您不会获得对 _SpillageRiskDescriptions
字段的引用。您将获得字段的 值,该字段是对(动态分配的)ArrayList
实例的引用。
这是因为
ArrayList
是引用类型吗?
是的,ArrayList
是一个引用类型,因此您会收到对特定 ArrayList
实例的引用。
int
也会发生同样的情况(例如?)
是和不是。 是的,您将收到int
字段的值。而no,int
是一个值类型,所以如果你修改了值而没有显式地把它设置回来,底层的字段不会受到影响。
【讨论】:
【参考方案6】:它与属性没有任何关系。
在这种情况下,您通过值传递对数组列表的引用。
如果你做类似的事情
void AddSomethingToArrayList(ArrayList spillageRiskDescriptions)
spillageRiskDescriptions.Add("Something");
那么对数组列表的引用将被修改。
如果你要做类似的事情
void ReassignArrayList(ArrayList spillageRiskDescriptions)
spillageRiskDescriptions = new ArrayList();
spillageRiskDescriptions.Add("Something");
那么对数组列表的原始引用根本不会改变。
但是,如果您要通过引用传递引用,例如:
void ReassignArrayListByRef(ref ArrayList spillageRiskDescriptions)
spillageRiskDescriptions = new ArrayList();
spillageRiskDescriptions.Add("Something");
那么您的属性最终会得到一个包含单个项目的新数组列表。
【讨论】:
以上是关于属性 - 按值或参考?的主要内容,如果未能解决你的问题,请参考以下文章