2010-02-24 59 views
6

将参数Value Type传递给c#中的函数是通过值,除非您在参数上使用ref或out关键字。但是这也适用于Reference TypesIList是否按价值传递?

具体而言,我有一个功能,需要一个IList<Foo>。传递给我函数的列表是否包含其包含对象副本的列表副本?或者对列表的修改是否也适用于呼叫者?如果是这样 - 我可以通过一个聪明的方式来传递一份副本吗?

public void SomeFunction() 
{ 
    IList<Foo> list = new List<Foo>(); 
    list.Add(new Foo()); 
    DoSomethingWithCopyOfTheList(list); 
    .. 
} 

public void DoSomethingWithCopyOfTheList(IList<Foo> list) 
{ 
    // Do something 
} 
+0

Thx为快速回复的人!就像我预料的那样。只是有一个不确定的时刻.. – stiank81 2010-02-24 09:21:47

+0

如果你想做一些聪明的事情,你可以做一个你自己的列表类,通过复制隐式投给IList。我能看到的唯一好处是,它在理论上可以防止您在传递集合时“忘记”复制副本。 – kyoryu 2010-02-24 09:43:32

+0

不 - 我不想那样做。IEnumerable的List构造函数看起来像它一样聪明。所有我需要.. – stiank81 2010-02-24 09:45:52

回答

16

所有参数是按值传递,除非你明确地使用refout。但是,当您传递引用类型的实例时,您会按值传递引用。即引用本身被复制,但由于它仍然指向同一个实例,所以仍然可以通过此引用修改实例。即该实例未被复制。参考是。

如果你想复制一份清单本身,List<T>有一个handy constructor,需要IEnumerable<T>

+1

此外,如果您更改函数体内的引用,则此更改在调用方中将不可见。从我+1。 – 2010-02-24 10:02:54

+1

@Aggelos:的确如此。这是通过价值传递的特性。 – 2010-02-24 10:17:23

3

该列表通过引用传递,因此如果您在SomeFunction中修改列表,则也会修改调用者的列表。

您可以通过创建一个新创建列表的副本:

var newList = new List<Foo>(oldList); 
2

您的列表通过引用传递。如果你想通过列表的副本,你可以这样做:

IList<Foo> clone = new List<Foo>(list); 

如果添加/删除克隆的元素不会改变显示 但要素的修改自己会考虑在这两个名单。

+0

+1的解释:'...但这些元素本身的修改将在这两个列表中考虑到.' – 2016-05-15 06:10:43

1

当您通过值传递引用类型(不带ref或out关键字)时,您可以在此方法内修改此引用类型,并且所有更改都会反映给调用者的代码。

解决你的问题,你可以明确地创建一个副本,这个副本传递给你的函数,或者你可以使用:

list.AsReadOnly(); 
0

当传递引用类型,你传递的参考。这是一个重要的概念。

如果传递参考

按地址,否则直接传递参考(指针)。

byval,你通过参考(指针)的副本。

引用不是引用的实例。引用与指针类似。

要传递引用类型实例的副本,首先必须自己创建副本并将引用传递给副本。因此,你将不会修改原始实例。

+2

我怀疑你可以把它放在一个更混乱的方式.. – Blindy 2010-02-24 09:23:25

+0

是更好的吗? – MaLio 2010-02-24 09:49:03

10

你并不孤单;这让很多人感到困惑。

以下是我喜欢的方式。

变量是一个存储位置。

变量可以存储某种特定类型的东西。

有两种类型:值类型和引用类型。

引用类型变量的值是对该类型对象的引用。

值类型变量的值是该类型的对象。

形式参数是一种变量。

形式参数有三种:值参数,参考参数和输出参数。

当您将变量用作参数值对应的参数时,该变量的值将被复制到与形式参数关联的存储中。如果该变量是值类型的,则创建该值的一个副本。如果变量是引用类型,则引用的副本被创建,并且这两个变量现在引用同一个对象。无论哪种方式,都会创建一个变量值的副本。

当您使用变量作为参数对应于out或ref参数时,该参数将成为变量的别名。当你说:

void M(ref int x) { ...} 
... 
int y = 123; 
M(ref y); 

你说的话是“x和y现在同一变量”。他们都提到相同的存储位置

我发现比理解别名是如何实际实现的更容易理解 - 通过将变量的托管地址传递给形式参数。

这是明确的吗?

+0

超级清晰!谢谢 :-) – stiank81 2010-02-24 21:13:52