属性 - 按值或参考?

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&lt;string&gt; 仍然允许突变。

如果客户关心的是能够一次遍历列表中的一件事,那么您可以这样做:

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 转换为 List。但是很高兴知道我在做什么是对的。 他提到的唯一在 .Net 2 中不可用的解决方案是自动属性,可以在 .Net 2 中手动轻松实现。您还需要将var 替换为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 字段的值。而noint 是一个值类型,所以如果你修改了值而没有显式地把它设置回来,底层的字段不会受到影响。

【讨论】:

【参考方案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");
      

那么您的属性最终会得到一个包含单个项目的新数组列表。

【讨论】:

以上是关于属性 - 按值或参考?的主要内容,如果未能解决你的问题,请参考以下文章

Mongoid 按值或默认值查询

在 C++11 中按值或引用使用 lambda 默认捕获的缺点?

按值或键进行嵌套数组搜索

Nosql中按值或条件值搜索记录,拥有1亿条记录

c ++按值或指向函数语法的指针传递数组

在 Scala 中按名称调用与按值调用,需要澄清