这是一个简单的问题(但我似乎无法在MSDN文档中找到答案)。
如果我在F#中将大型序列和对象作为函数参数传递,它们是否总是按值复制,除非我提供了byref关键字?问题是,我无意修改参数,但与此同时,我不希望每次调用函数时都要复制大对象。
答案 0 :(得分:10)
我不同意人们在这里使用的术语。
我会将其表达为:默认情况下,.NET中的所有内容都是按值传递,例外情况是例如F#中的ref
和out
参数以及F#中的byref
参数。但是,序列和数组以及大多数其他类型(除了结构和基元)都是对象引用,这意味着当您将它们作为参数传递(按值)时,您只传递对实体的引用,并且调用者和被调用者共享堆上的同一个实际实体。所以引用是通过值传递的,但引用本身只是小事,而它们指向的对象实体(包含所有数据)是在堆上共享的大事。
答案 1 :(得分:5)
如评论中所述,F#使用与C#基本相同的机制。这意味着只有值类型(原始值,如int
,float
和结构)被复制到堆栈上,所有其他类型都通过引用传递 - 这包括所有集合(序列seq<'T>
,数组'T[]
以及不可变功能列表list<'T>
)。
当你使用不可变类型(函数list<'T>
和seq<'T>
没有副作用)时,传递引用和复制除了性能之外的对象之间没有区别 - 运行时总是通过它们作为参考,但你不需要担心这个,除非你正在优化一些程序(如果你可以改变传递机制,它不会改变行为)。
对于(可变)数组,您可以通过查看John Palmer的答案中的示例,轻松地看到该行为是通过引用传递的。
作为旁注,如果要创建对堆栈分配的可变变量的引用,则使用byref
关键字(或类型)。这意味着,您可以使用它来传递对值类型变量的引用:
let bar (a:byref<int>) = // takes reference to a (stack-allocated) 'int' variable
a <- 42 // mutate the variable
let mutable foo = 10 // declare a mutable variable
bar &foo // pass reference to the `bar` function
如果使用byref
传递集合,则实际上是在传递对堆栈分配的引用的引用,该引用实际上指向堆分配的数据结构。但是,byref
在F#中很少使用 - 在调用C / C ++或COM库时,您主要需要它来实现互操作性。
答案 2 :(得分:2)
默认情况下,序列/列表/数组通过引用传递
例如
let mutate (arr:int[]) =
arr.[0] <- 1
let t = Array.zerocreate 1
mutate t
printfn "%i" (t.[0]) //prints 1
答案 3 :(得分:0)
由于值是不可变的,因此您传递的相同序列将被修改或用于.. 每次都不会复制对象..
虽然如果您修改了Sequence,那么将使用新值
创建一个新序列