从C#调用F#时,OO替代F#中的多态性

Nes*_*ure 7 c# oop f#

我想创建一个库,它将使用用户定义的函数创建对象,并使用另一个用户定义的函数进行修改.

我有一个OCaml背景,看到一个相当简单的方法来实现它:

//user side
type userType = { mutable time : float }

let userInitialization () = { time = 0. }    
let userModification t = t.time <- t.time+1.

//my side
let algo initialization modification n =
    let a = Array.init n (fun _ -> initialization ())
    modification a.[0]
    a
Run Code Online (Sandbox Code Playgroud)

问题是我希望从C#中轻松调用该库:将函数作为参数可能是一个坏主意.

接口和抽象类(将继承userType)似乎是通常的OO解决方案,但如果我不知道(尚未定义的)userType在我的函数内部进行初始化步骤,我就无法初始化对象.

我能想到的唯一解决方法是向用户询问他userType作为参数的实例,该实例将用于调用初始化,但这似乎非常不优雅.

有没有办法解决这个问题?

Mar*_*ann 5

您可以定义这样的界面:

type IInitAndModify<'T> =
    abstract member CreateNew : unit -> 'T
    abstract member Modify : item : 'T -> unit
Run Code Online (Sandbox Code Playgroud)

也许还有一个用户可以用来将它传递给你的实现的对象:

type Algo<'T> () =
    member this.Execute (initAndModify : IInitAndModify<'T>, n) =
        algo initAndModify.CreateNew initAndModify.Modify n
Run Code Online (Sandbox Code Playgroud)

从C#开始,它看起来像这样:

public interface IInitAndModify<T>
{
    T CreateNew();
    void Modify(T item);
}

public class Algo<T>
{
    public Algo();

    public T[] Execute(IInitAndModify<T> initAndModify, int n);
}
Run Code Online (Sandbox Code Playgroud)

客户端开发人员可以像这样使用它:

public class UserType
{
    public float Time { get; set; }
}

public class UserInitAndModify : IInitAndModify<UserType>
{
    public UserType CreateNew()
    {
        return new UserType { Time = 0 };
    }

    public void Modify(UserType item)
    {
        item.Time++;
    }
}
Run Code Online (Sandbox Code Playgroud)

并写一个这样的程序:

static void Main(string[] args)
{
    var a = new Algo<UserType>();
    var values = a.Execute(new UserInitAndModify(), 10);
    foreach (var v in values)
        Console.WriteLine(v.Time);
}
Run Code Online (Sandbox Code Playgroud)

运行上面的Main方法时,输出是这样的:

1
0
0
0
0
0
0
0
0
0
Press any key to continue . . .
Run Code Online (Sandbox Code Playgroud)


The*_*ght 5

我倾向于拒绝你不应该在暴露给C#的库中公开函数作为参数的想法,这样做变得非常普遍,你只需要看看LINQ,TPL等等.我不会想太多C#开发人员会因此而害怕.

但是,我建议你避免使用带有cur的参数向C#公开函数,因为这些函数根本不方便使用.

您可以非常轻松地将算法包装在一个函数中,该函数接受以tupled形式接受的参数并公开System.Func和转换System.Action为C#.

let csAlgo (initialisationFunc : System.Func<'a>, 
            modificationFunc : System.Action<'a>, 
            n : int) =
    algo (initialisationFunc.Invoke) (modificationFunc.Invoke) n
Run Code Online (Sandbox Code Playgroud)

在C#中你可以这样做:

var res = Module.csAlgo(() => new UserType(0), t => t.Time = t.Time + 1, 16);
Run Code Online (Sandbox Code Playgroud)

另外,您还可以使用该CompiledName属性来获得每种语言的套管约定.标记你的功能

[<CompiledName("ExampleFunction")>]
let exampleFunction () = 1
Run Code Online (Sandbox Code Playgroud)

然后在C#中,它看起来像这样:

var num = Module.ExampleFunction();
Run Code Online (Sandbox Code Playgroud)