Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/335.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 设计F#库以供F#和C使用的最佳方法#_C#_F# - Fatal编程技术网

C# 设计F#库以供F#和C使用的最佳方法#

C# 设计F#库以供F#和C使用的最佳方法#,c#,f#,C#,F#,我正试图用F#设计一个库。图书馆应便于F#和C#使用 这就是我被卡住的地方。我可以让它变得F#friendly,也可以让它变得C#friendly,但问题是如何让它对双方都友好 这里有一个例子。假设我在F#中有以下函数: let compose(f:'T->'TResult)(a:'TResult->unit)=f>>a 这在F#中完全可用: 让我们使用composeinfsharp()= 让composite=compose(趣味项目->项目.ToString)(趣味项目->打印“%A”项目

我正试图用F#设计一个库。图书馆应便于F#和C#使用

这就是我被卡住的地方。我可以让它变得F#friendly,也可以让它变得C#friendly,但问题是如何让它对双方都友好

这里有一个例子。假设我在F#中有以下函数:

let compose(f:'T->'TResult)(a:'TResult->unit)=f>>a
这在F#中完全可用:

让我们使用composeinfsharp()=
让composite=compose(趣味项目->项目.ToString)(趣味项目->打印“%A”项目)
复合“foo”
复合“棒”
在C#中,
compose
函数具有以下签名:

FSharpFunc组合(FSharpFunc f,FSharpFunc a);
但当然,我不希望签名中出现
FSharpFunc
,我想要的是
Func
Action
,如下所示:

动作组合2(功能f,动作a);
为此,我可以创建如下
compose2
函数:

let compose2(f:Func)(a:Action(f.Invoke>>a.Invoke)
现在,这在C#中完全可用:

void UseCompose2FromCs()
{
compose2((字符串s)=>s.ToUpper(),Console.WriteLine);
}
但是现在我们在使用F#中的
compose2
时遇到了问题!现在我必须将所有标准的F#
funs
包装到
Func
Action
,如下所示:

让我们使用compose2infsharp()=
设f=Func(趣味项目->项目.ToString())
让a=动作(趣味项目->打印“%a”项目)
设composite2=compose2 f a
composite2.调用“foo”
composite2.调用“bar”
问题是:我们如何为F和C用户提供一流的F编写的图书馆体验

到目前为止,我想不出比这两种方法更好的方法:

  • 两个独立的程序集:一个面向F#用户,另一个面向C#用户
  • 一个程序集,但名称空间不同:一个用于F#用户,第二个用于C#用户
  • 对于第一种方法,我会这样做:

  • 创建一个F#项目,将其命名为FooBarFs并将其编译成FooBarFs.dll

    • 将该库完全面向F#用户
    • 从.fsi文件中隐藏所有不必要的内容
  • 创建另一个F#项目,调用if FooBarCs并将其编译成FooFar.dll

    • 在源代码级别重用第一个F#项目
    • 创建.fsi文件,该文件隐藏该项目的所有内容
    • 创建.fsi文件,该文件以C#方式公开库,使用C#习惯用法表示名称、名称空间等
    • 创建委托给核心库的包装器,在必要时进行转换
  • 我认为使用名称空间的第二种方法可能会让用户感到困惑,但是您只有一个程序集

    问题:这些都不是理想的,也许我缺少了某种编译器标志/开关/属性 或者某种把戏,有更好的方法吗

    问题是:是否有其他人试图实现类似的目标,如果是,您是如何做到的

    编辑:澄清一下,问题不仅在于函数和委托,还在于C#用户使用F#库的总体体验。这包括C#固有的名称空间、命名约定、习惯用法等。基本上,C#用户不应该检测到库是用F#编写的。反之亦然,F#用户应该感觉到就像处理一个C#图书馆


    编辑2:

    从到目前为止的回答和评论中,我可以看出我的问题缺乏必要的深度, 可能主要是因为只使用了一个例子,其中F#和C#之间存在互操作性问题 出现函数值的问题。我认为这是最明显的例子,因此 让我用它来问这个问题,但同样给人的印象是 我唯一关心的问题是

    让我提供更多具体的例子。我已经通读了最优秀的例子 文档(非常感谢@gradbot!)。文档中的指导原则(如果使用)确实解决了这个问题 一些问题,但不是全部

    该文件分为两个主要部分:1)针对F#用户的指南;和2)关于 以C#用户为目标。它甚至没有试图假装有一套制服 方法,这正好呼应了我的问题:我们可以针对F,我们可以针对C,但问题是什么 针对两者的实际解决方案

    提醒一下,我们的目标是创建一个用F#编写的库,可以从 F#和C#两种语言

    这里的关键词是惯用的。问题不在于可能实现的通用互操作性 使用不同语言的库

    现在来看看我直接从中得到的例子

  • 模块+函数(F#)与名称空间+类型+函数

    • F#:一定要使用名称空间或模块来包含类型和模块。 惯用用法是将函数放在模块中,例如:

      // library
      module Foo
      let bar() = ...
      let zoo() = ...
      
      
      // Use from F#
      open Foo
      bar()
      zoo()
      
    • C#:一定要使用名称空间、类型和成员作为您的主要组织结构 组件(与模块相反),用于香草.NETAPI

      这与F#准则不符,这个例子需要 要重新编写以适合C#用户:

      []
      Foo型=
      静态成员栏()。。。
      静态成员zoo()=。。。
      
      但这样做,我们打破了F的惯用用法,因为 我们不能再使用
      bar
      zoo
      而不在其前面加上
      Foo

  • 元组的使用

    • F#:在适合返回值时,一定要使用元组

    • C#:避免在vanilla.NETAPI中使用元组作为返回值

  • 异步的

    • F:你喜欢我们吗
      [<AbstractClass; Sealed>]
      type Foo =
          static member bar() = ...
          static member zoo() = ...