How do I properly implement a property in F#?

2.9k views Asked by At

Consider my first attempt, a simple type in F# like the following:

type Test() =
    inherit BaseImplementingNotifyPropertyChangedViaOnPropertyChanged()
    let mutable prop: string = null
    member this.Prop
        with public get() = prop
        and public set value =
            match value with
                | _ when value = prop -> ()
                | _ -> 
                    let prop = value
                    this.OnPropertyChanged("Prop")

Now I test this via C# (this object is being exposed to a C# project, so apparent C# semantics are desirable):

[TestMethod]
public void TaskMaster_Test()
{
    var target = new FTest();
    string propName = null;
    target.PropertyChanged += (s, a) => propName = a.PropertyName;
    target.Prop = "newString";

    Assert.AreEqual("Prop", propName);
    Assert.AreEqual("newString", target.Prop);

    return;
}

propName is properly assigned, my F# Setter is running, but the second assert is failing because the underlying value of prop isn't changed. This sort of makes sense to me, because if I remove mutable from the prop field, no error is generated (and one should be because I'm trying to mutate the value). I think I must be missing a fundamental concept.

What's the correct way to rebind/mutate prop in the Test class so that I can pass my unit test?

3

There are 3 answers

2
kvb On BEST ANSWER

Try this:

type Test() =
    inherit BaseImplementingNotifyPropertyChangedViaOnPropertyChanged()
    let mutable prop: string = null
    member this.Prop
        with public get() = prop
        and public set value =
            match value with
                | _ when value = prop -> ()
                | _ -> 
                    prop <- value
                    this.OnPropertyChanged("Prop")

You need to make the binding mutable and then alter its value in your setter. In your initial code, you were just creating a new binding (also called prop) within your setter, so no change was visible.

0
TwentyMiles On

In your pattern match you are actually binding a new value with

let prop = value

When you bind a value like this with the same name, it will shadow the other value for the scope of the newly declared one. I believe what you actually want to do is this:

prop <- value
1
Tomas Petricek On

As a side-note, I would probably use if .. then instead of the match construct as it makes the code more succinct (patterh matching is especially valuable when you need to test the value agains multiple complex patterns). Also, public is the default access for member, so you can make the code a bit more succinct:

type Test() = 
    inherit BaseImplementingNotifyPropertyChangedViaOnPropertyChanged() 
    let mutable prop : string = null 
    member this.Prop 
        with get() = prop 
        and set(value) = 
            if value <> prop then 
               prop <- value 
               this.OnPropertyChanged("Prop")