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

use*_*043 11 c# f# record

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

let myRecord = {| SomeInteger = 5 |}
Run Code Online (Sandbox Code Playgroud)

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

var someInteger = myRecord.SomeInteger;
Run Code Online (Sandbox Code Playgroud)

如果我有F#函数,反过来又说:

let unwrap (record : {| SomeInteger : int |}) = record.SomeInteger
Run Code Online (Sandbox Code Playgroud)

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

var unwrapped = unwrap(new { SomeInteger = 5 });
Run Code Online (Sandbox Code Playgroud)

但这没有编译。我在RFC中注意到该功能,它表示“该功能必须实现与C#匿名对象(来自C#3.0)的兼容性”,但并未以哪种方式具体提及。支持吗?

exy*_*xyi 2

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

您可以查看以下示例:https://sharplab.io/#v2 :EYLgxg9gTgpgtADwGwBYA0AbEAzAzgHwFgAoDGAFwAIBbATwCUZIoATSgXkoG99KBlCNRgBJAHbkYAcxhQOlAKyV8AXxJkqAV1EB3KAEMADgB4A5HoB8lABSxmbEN14 ChYidNkOzS5QEo5t6BYAOmcRcSkZEiA==

我将以下 F# 粘贴到其中,让它编译,然后反编译为 C# 代码:

let createRecord () = {| SomeInteger = 5 |}
let unwrap (record : {| SomeInteger : int |}) = record.SomeInteger
Run Code Online (Sandbox Code Playgroud)

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

let unwrap<'a> (record : {| SomeInteger : 'a |}) = record.SomeInteger
Run Code Online (Sandbox Code Playgroud)

翻译后的函数看起来像这样

public static <>f__AnonymousType2453178905<int> createRecord()
{
    return new <>f__AnonymousType2453178905<int>(5);
}
public static int unwrap(<>f__AnonymousType2453178905<int> record)
{
    return record.SomeInteger;
}
Run Code Online (Sandbox Code Playgroud)

这意味着:

  • 我会避免在公共 API 中使用匿名记录
  • 如果您确实想使用它们,则必须为它们提供工厂函数。
    • 它们的所有参数可以是通用的,例如let createMyRecord a = {| SomeInteger = a |}可以很好地工作
    • 虽然它可以在 C# 中使用,但看到参数应该是类型<>f__AnonymousType3239938913<<A>j__TPar, <B>j__TPar, <SomeInteger>j__TPar>不会有太大帮助
  • 如果其他人已经构建了 API 并且现在您必须使用,您仍然可以使用反射创建实例
    • MethodInfo您可以通过调用method.GetParameters()[0].ParameterType或通过Type.GetType("namespace.typenamenumberOfGenericArgs来获取类型”)
    • 该类型有一个构造函数,属性按字母顺序排列。您可以使用Activator.CreateInstance(...)它来调用它。