简体   繁体   中英

F# Immutable Class Interop

How do F# immutable types interface with C#. I'm just starting to learn F# and I'd like to mix it in with some C# code I have, but I want my F# classes to be immutable.

Let's say we're making a Vector class in F#. Vector.X and Vector.Y should be re-assignable, but only be returning a new Vector class. In C# this would take allot of legwork to make .WithX(float x) clone the existing object and return a new one. Is there an easy way to do this in F#?

I've been searching for some time and I can't seem to find any docs on this. So any help would be great.

And finally, if I imported this class into C# what would its interface look like? Will the F# code restrict me from doing something stupid like Vector.X = 10 ?

This will look similar regardless of whether it's C# or F#.

You say "in C# it will take legwork", but cmon, I think

Vector WithX(float x) { return new Vector(x, this.Y); }

is it, right?

In both C# and F#, to prevent assignment to the X property, you author a property with a 'getter' but no 'setter'.

I think you're making all of this out to be harder than it is, or maybe I'm misunderstanding what you're asking.

EDIT

For the (I think rare) case of where there are 20 field and you may want to change just a small arbitrary subset of them, I found a cute hack to use F# and C# optional parameters together nicely.

F# Code:

namespace global

open System.Runtime.InteropServices

type Util =
    static member Some<'T>(x:'T) = Some x

type MyClass(x:int, y:int, z:string) =
    new (toClone:MyClass, 
         [<Optional>] ?x, 
         [<Optional>] ?y, 
         [<Optional>] ?z) = 
            MyClass(defaultArg x toClone.X, 
                    defaultArg y toClone.Y, 
                    defaultArg z toClone.Z)
    member this.X = x
    member this.Y = y
    member this.Z = z

F# client code:

let a = new MyClass(3,4,"five")
let b = new MyClass(a, y=44)  // clone a but change y

C# client code:

var m = new MyClass(3, 4, "five");
var m2 = new MyClass(m, y:Util.Some(44)); // clone m but change y

That is, optional parameters are a nice way to do this, and while C# optional parameters have some limitations, you can expose F# optional parameters in a way that works ok with C#, as suggested above.

F# Record types have a built in way of doing exactly what you're asking:

type Vector = {X:float; Y:float}

let v1 = {X=1.; Y=2.}
let v2 = {v1 with X=3.}

How that interops with C#, I'm not sure ( edit: see Brian's comment ).

Vector will be immutable from any .NET language, since X and Y are implemented as getters without setters.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM