views:

248

answers:

2

Hi,

One of the libraries I'm using defines this in C#:

public ushort GetParameterSet(string name, out ParameterSet parameterSet)

I'm trying to call this from F#:

let parameterSet = new ParameterSet();
let retVal = currentPanel.GetParameterSet(name, ref parameterSet);

However, even though the parameterSet is set to an instance of that class with valid data in the C# method, it does not change in F#.

What am I missing here?

+3  A: 

Try

// let parameterSet = null;
let retval, parameterSet = currentPanel.GetParamterSet(name);

You shouldn't pass an instance as ref parameter, when the method awaits an out paramter (which upon calling should be an unassigned reference preceded by the out keyword).

Tamás Szelei
Wow nice... So why does fsharp have the ref keyword and allow it if it does not work that way (yours does btw)
TimothyP
The distinction between `ref` and `out` is purely a C# distinction that isn't visible "outside" - for example, VB doesn't have it, and will treat both C# `ref` and `out` as `ByRef`. And it is certainly fine to pass an initialized variable to an `out`-parameter - there's no requirement that the variable _must_ be unassigned, even in C#.
Pavel Minaev
I knew it you *can*, I meant you *shouldn't*. But I was not aware of that this is C#-only, I thought it's a CLR thing. Thanks for clarification!
Tamás Szelei
Why shouldn't you? I can have a variable with some value that I have already used, and I just want to overwrite it with a new one. Passing an assigned variable to `out`-parameter is not any different from using `operator=` to assign a new value to the same variable. Both are perfectly legal, and have valid reasons. Perhaps you're confusing this with the requirement that variable passed to `ref` _must_ be initialized, while `out` does not have such limitation?
Pavel Minaev
+6  A: 

First of all, why your code doesn't work as is: ref doesn't have the same meaning in F# as it does in C#. In F#, 'a ref is a type. It's not magical, either - it's really just a record with a single field contents, defined as follows:

type 'a ref = { mutable contents : 'a }

It is not a pointer or reference to a local variable. Thus, when you write this:

let x = 0
let y = ref x

you do not have variable y referencing x. Instead, you have variable y of type int ref, with the value of contents initialized to the value that x had (that is 0). You can change that:

y.contents <- 1

and this will not change the value of x. It will only change the value of contents

F# also provides some syntactic sugar for 'a ref. Specifically, this:

y.contents <- y.contents + 1

can be written shorter as:

y := !y + 1

Thus := is a shorthand for assigning to contents, and ! is a shorthand for reading its value.

See Reference Cells in MSDN for more information on ref.

Now F# has a magic cast associated with 'a ref type, that lets you pass an instance of that type to a foreign function that expects a byref argument (both ref and out in C# map to byref in IL). In this case, if function changes the value of the argument, the value of contents in your ref instance changes accordingly. In your example, ref parameterSet created a new instance of ref, passed it to function which changed it, and then discarded it. What you should have done is this:

let parameterSet = ref(new ParameterSet())
let retVal = currentPanel.GetParameterSet(name, parameterSet)
...
// use parameterSet.contents as needed

Alternatively, you could use let mutable to declare a mutable local variable, and then use the magical & operator to pass it to the function directly as byref:

let mutable parameterSet = new ParameterSet()
let retVal = currentPanel.GetParameterSet(name, &parameterSet)
Pavel Minaev