I'm trying to create a small piece of code in F#. The idea is that I would have an interface which defines a few methods, properties, and events. Using this interface I want to define an abstract class (having a generic type) which would implement some properties/methods but not all of the members defined in the interface. The responsibility to define all of the members would be on the derived classes.
So far I'm stuck with the interface and the abstract class. The problems I'm having:
The interface compiles fine but the abstract class has several errors. The messages are included in code
All comments, corrections, hints, etc are appreciated. Since this is my first trial on F# there are probably a lot of mistakes to point out.
The code I have so far
Interface definition
namespace A
type IValueItem =
interface
[<CLIEvent>]
abstract member PropertyChanged :
Control.IEvent<System.ComponentModel.PropertyChangedEventHandler,
System.ComponentModel.PropertyChangedEventArgs>
abstract ConvertedX : double with get, set
abstract Y : double with get, set
abstract member CreateCopy : obj
abstract member NewTrendItem : obj
end
And the abstract class
namespace A
[<AbstractClass>]
type ValueItem<'TX>() =
[<DefaultValue>]
val mutable _y : double
let _propertyChanged = new Event<_>()
// ERROR: This type is not an interface type
// ERROR: The type 'obj' is not an interface type
// ERROR: The type 'IValueItem' is not defined
interface IValueItem with
// ERRROR No abstract or interface member was found that corresponds to this override
[<CLIEvent>]
member this.PropertyChanged :
Control.IEvent<System.ComponentModel.PropertyChangedEventHandler,
System.ComponentModel.PropertyChangedEventArgs>
= _propertyChanged.Publish
// This definition is incomplete, should be abstract
member ConvertedX : double with get, set
// ERROR: Incomplete structured construct at or before this point in pattern
member CreateCopy() : obj
member NewTrendItem() : obj
abstract X : 'TX with get, set
member this.Y
with get() = this._y
and set(value) =
if this._y <> value then
this._y <- value
this.NotifyPropertyChanged("Y")
member this.NotifyPropertyChanged(propertyName) =
this.PropertyChanged.Trigger(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName))
The Abstract class and Interface both are used to have abstraction. An abstract class contains an abstract keyword on the declaration whereas an Interface is a sketch that is used to implement a class.
An abstract class can contain both abstract and non-abstract methods, whereas an Interface can have only abstract methods. Abstract classes are extended, while Interfaces are implemented.
Type of variables: Abstract class can have final, non-final, static and non-static variables. The interface has only static and final variables. Implementation: Abstract class can provide the implementation of the interface. Interface can't provide the implementation of an abstract class.
The abstract class definition does not recognize the interface definition
The interface must be declared above the abstract class, either: in a referenced project/assembly, in a file above your current file (order of files matters), or above in the same file.
I'm not sure how to define the abstract members for the interface in the abstract class
Unfortunately we have to implement all members of the interface, to keep the behavior you want you can have the implementation call an abstract member of the class that has the same signature:
type IValueItem =
[<CLIEvent>]
abstract PropertyChanged :
Control.IEvent<System.ComponentModel.PropertyChangedEventHandler,
System.ComponentModel.PropertyChangedEventArgs>
abstract ConvertedX : double with get, set
abstract Y : double with get, set
abstract CreateCopy : obj
abstract NewTrendItem : obj
[<AbstractClass>]
type ValueItem<'TX>() =
let mutable y = 0.0
let propertyChanged = Event<_, _>()
interface IValueItem with
[<CLIEvent>]
member __.PropertyChanged : Control.IEvent<_, _> = propertyChanged.Publish
member this.ConvertedX
with get() = this.ConvertedX
and set(x) = this.ConvertedX <- x
member this.CreateCopy = this.CreateCopy
member this.NewTrendItem = this.NewTrendItem
member this.Y
with get() = this.Y
and set(y) = this.Y <- y
abstract ConvertedX : double with get, set
abstract CreateCopy : obj
abstract NewTrendItem : obj
member this.Y
with get() = y
and set(value) =
if y <> value then
y <- value
this.NotifyPropertyChanged("Y")
abstract X : 'TX with get, set
member this.NotifyPropertyChanged(propertyName) =
propertyChanged.Trigger(this, System.ComponentModel.PropertyChangedEventArgs(propertyName))
The Y
property is defined twice which looks a bit odd at first. The interface implementation defers to the implementation on the class - this means that to access Y
you won't need to upcast an instance of the class to the interface, I've done this to maintain the same behavior as your initial example
To your comment:
To get a virtual member on an abstract class you need to declare the member as abstract and provide a default implementation, here's an example to match the code in your comment:
type IValueItem =
abstract NewTrendItem : unit -> obj
[<AbstractClass>]
type ValueItem<'TX>() =
interface IValueItem with
member this.NewTrendItem() = this.NewTrendItem()
abstract NewTrendItem : unit -> obj
default __.NewTrendItem() = null
type NumberItem() =
inherit ValueItem<double>()
override __.NewTrendItem() = new NumberItem() :> obj
Generally OO F# is used to interact with the rest of the .NET world - that looks like the case here. But if the code isn't interacting with another .NET API that requires an OO interface, you may find modelling the problem with a functional approach might result in some cleaner code. OO in F# might not give an initial good impression of the language (although personally I quite like the explicitness and brevity of it)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With