如何在C#中创建F#匿名记录?

时间:2019-04-04 20:14:43

标签: c# f# record

例如,我可以看到是否创建了一个新的匿名记录。

let myRecord = {| SomeInteger = 5 |}

然后,如果它暴露在C#中,那么我可以用

var someInteger = myRecord.SomeInteger;

反之,如果我有F#函数,说:

let unwrap (record : {| SomeInteger : int |}) = record.SomeInteger

并且它已经暴露在C#中,如何从C#实例化此函数的参数并调用它?我天真地尝试只在其中放置一个C#匿名类型,即。

var unwrapped = unwrap(new { SomeInteger = 5 });

但是没有编译。我在RFC for the feature中注意到说“该功能必须实现与C#匿名对象(从C#3.0起)的兼容性” ,但没有以哪种方式具体提及。支持吗?

1 个答案:

答案 0 :(得分:1)

不幸的是,这似乎基本上是不可能的。我们可以使用很棒的工具https://sharplab.io/来看看发生了什么以及API的外观。

您可以在以下位置查看示例:https://sharplab.io/#v2:EYLgxg9gTgpgtADwGwBYA0AbEAzAzgHwFgAoDGAFwAIBbATwCUZIoATSgXkoG99KBlCNRgBJAHbkYAcxhQOlAKyV8AXxJkqAV1EB3KAEMADgB4A5HoB8lABSxmbEN14ChYidNkOzS5QEo5t6BYAOmcRcSkZEiA==

我已将以下F#粘贴到其中,并使其进行编译,然后反编译为C#代码:

let createRecord () = {| SomeInteger = 5 |}
let unwrap (record : {| SomeInteger : int |}) = record.SomeInteger

我们可以看到F#编译器生成了名为<>f__AnonymousType2453178905的类。名称是主要问题,因为您无法在C#:/(AFAIK)中引用它。顺便说一句,有趣的是SomeInteger的类型是泛型的,因此您可以编写unwrap函数的泛型,它仍然可以工作:

let unwrap<'a> (record : {| SomeInteger : 'a |}) = record.SomeInteger

翻译后的函数如下

public static <>f__AnonymousType2453178905<int> createRecord()
{
    return new <>f__AnonymousType2453178905<int>(5);
}
public static int unwrap(<>f__AnonymousType2453178905<int> record)
{
    return record.SomeInteger;
}

这意味着:

  • 我要避免在公共API中使用匿名记录
  • 如果您真的想使用它们,则必须为其提供工厂功能。
    • 它们可以在所有参数中通用,例如let createMyRecord a = {| SomeInteger = a |}会很好地工作
    • 虽然可以在C#中使用,但看到参数应该为<>f__AnonymousType3239938913<<A>j__TPar, <B>j__TPar, <SomeInteger>j__TPar>类型的提示
  • 如果其他人已经构建了API,但现在您必须使用它,则仍然可以使用反射来创建实例
    • 您可以通过调用MethodInfomethod.GetParameters()[0].ParameterType numberOfGenericArgs“从Type.GetType("namespace.typename获取类型
    • 该类型具有构造函数,属性按字母顺序存在。您可以使用Activator.CreateInstance(...)来调用它。