在C#中,事件参数是按引用传递还是按值传递的?

15

一个相当简单的问题(我认为),但我似乎没看到已有的答案。我知道一些值是通过值传递的(比如int和long),而当您将它们传递给函数时,另一些值是通过引用传递的(比如字符串)。

在我的程序中,我使用了后台工作线程,以便在后台进行长时间的处理时,GUI不会锁定。我需要从另一个文件向UI线程传回数据,因此我一直在使用事件来处理。现在我需要将一个字符串数组的列表发送回GUI线程以在那里处理,我很担心它会被如何处理。基本上,在工作线程中,我有一个循环,将填充该列表,通过事件处理程序将其发送回GUI,然后清除它,以便能够在下一次循环中重新填充它并重新开始。

我担心当我这样做时,如果列表是通过引用传递的,那么在UI线程上,我会认为它会在中途读取时被清除,因为工作线程仍将在后台清除它。在这种情况下,传递方式将更可取,并且我可以找到方法来强制执行它(将其复制到某个持有器数组中或添加互斥量或类似的东西),但我认为知道事件参数是否通常通过引用或值传递是有好处的,或者它是否就像方法一样,将其作为通常传递的参数传递?


4
你所知道的是不正确的。在C#中,字符串和其他东西也是按值传递的。实际上,除非你明确地按引用传递,否则所有东西都是按值传递的。 - Brian Rasmussen
请查看MSDN说明的链接:http://msdn.microsoft.com/zh-cn/library/system.eventargs%28v=vs.80%29.aspx - MethodMan
我想我把它和被引用赋值的数组混淆了(即(temparray = array1)只是让temparray指向array1,但要创建一个新的数组,我需要(temparray = array1.clone())或类似的东西)。 - Xantham
1
@Xantham:数组是引用类型。因此,array1的值只是一个引用。所有赋值都只是将一个表达式的值复制到变量(或属性)中。 - Jon Skeet
4个回答

22
我知道一些值是通过值传递的(如int和long),而其他值是通过引用传递的(如字符串)当您将它们传递给函数时。
不是这样。默认情况下,所有内容都是按值传递的-但是当您使用引用类型时,“所有内容”都是一个引用。该引用按值传递。这与按引用传递不同。有关详细信息,请参见我的参数传递文章
事件参数完全相同-任何引用都是按值传递的,假设相应的委托不使用outref参数。
编辑:因此,为了解决您的问题:是的,如果您的事件参数是可变的,并且您要在不同的线程上执行操作,则应首先创建副本...或者,传递现有引用,然后在工作线程中创建新的(空)列表。

看起来很多人在术语方面被严重误导了。=) - Vaughan Hilts
每天都有新的学习!我之前认为通过引用传递对象参数是多余的。感谢您的告知! - Rob H
所以,只是为了确保“引用按值传递”和“按引用传递”。我是否可以这样说,“引用按值传递”意味着如果引用所指向的任何内容的地址为0x1337,则会按值传递0x1337(地址)。因此,“按引用传递”将是我始终拥有指向引用所引用的任何内容的东西,无论它是否在地址0x1337或其他地方? - Xantham
@Xantham:老实说,谈论地址并不是很有帮助。最好展示实际代码并询问其效果。(但请先阅读我的文章,希望能解决问题。) - Jon Skeet

4

默认情况下,参数本身按值传递。但是,根据它们的类型,它们可以是对您正在使用的实际值的值或引用。

请注意,这与通常所说的“按引用传递”不同,因为实际传递给参数的值是复制的(即“按值传递”)。但是,效果类似,如果您在方法中更改了引用的对象,则在方法外部(在调用方法的代码中)也会看到更改。

现在,在按值传递时,事件参数没有任何特殊之处;值是复制还是仅复制其引用完全取决于它们的类型。因此,如您所说,intlong参数(以及一些其他任何struct类型)都是值类型,而其他类型,如string(和任何类实例)都是引用类型。

请注意,C#中也可以进行真正的“按引用传递”,但这需要使用ref关键字


3
在标准的事件模式中,有两个参考文本被传递进来:
 void FormClosing(object sender, FormClosingEventArgs e) { ... }

这两个引用是“按值传递”的,例如使用sender = null将不会影响处理方法之外的内容。

但你可以轻松地将一个值传回去:

void FormClosing(object sender, FormClosingEventArgs e)
{
    ...
    e.Cancel = true;  // this will pass back to the caller
}

1
事件参数根据参数类型和事件处理程序委托的签名(in、out或ref)进行传递 - 如果是类,则传递引用的副本,如果是结构,则传递值的副本(假设签名未指定out或ref)。
事件参数通常是一个类(通常继承自EventArgs),经常用于向调用者返回值,例如eventArgs.DoCancel。

2
因为这是错误的。默认情况下,引用类型是通过引用的副本传递的。这是一个重要的区别,很少有人能够理解它。 - Ed S.
2
@DannyVarod:引用的副本是按值传递的。这不是“按引用传递”的意思。这是一个重要的区别,你和OP都没有做出来。 - Jon Skeet
3
正确答案是“默认情况下,一切都是按值传递的。对于引用类型,传递的是引用本身的值。” - Ed S.
1
@JonSkeet 我觉得我的回答已经很清楚了,如果还不明白的话,我会进行编辑。 - Danny Varod
1
@DannyVarod:基本上,虽然您的答案使用“按引用传递”一词来表示默认行为,但这可能是错误的 :) - Jon Skeet
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接