如何在 F# 中使用属性注释接口 属性 get 和 set

How to annotate interface property get and set with attributes in F#

如何将以下COM接口转换为F#?我不知道如何注释 属性 的 getset

此外,对于 COM 互操作,我是否需要用 DispId 注释 属性 本身及其 get

[ComImport, TypeLibType((short)0x1040), Guid("F935DC23-1CF0-11D0-ADB9-00C04FD58A0B")]
private interface IWshShortcut
{
    [DispId(0)]
    string FullName { [return: MarshalAs(UnmanagedType.BStr)] [DispId(0)] get; }
    [DispId(0x3e8)]
    string Arguments { [return: MarshalAs(UnmanagedType.BStr)] [DispId(0x3e8)] get; [param: In, MarshalAs(UnmanagedType.BStr)] [DispId(0x3e8)] set; }
    [DispId(0x3ec)]
    string RelativePath { [param: In, MarshalAs(UnmanagedType.BStr)] [DispId(0x3ec)] set; }
    [DispId(0x3ee)]
    int WindowStyle { [DispId(0x3ee)] get; [param: In] [DispId(0x3ee)] set; }
    [DispId(0x3ef)]
    string WorkingDirectory { [return: MarshalAs(UnmanagedType.BStr)] [DispId(0x3ef)] get; [param: In, MarshalAs(UnmanagedType.BStr)] [DispId(0x3ef)] set; }
    [TypeLibFunc((short)0x40), DispId(0x7d0)]
    void Load([In, MarshalAs(UnmanagedType.BStr)] string PathLink);
    [DispId(0x7d1)]
    void Save();
}

这是一个正确的翻译,虽然不是字面意思:

[<ComImport; Guid("F935DC23-1CF0-11D0-ADB9-00C04FD58A0B"); TypeLibType(0x1040s)>]
type private IWshShortcut =
  [<DispId(0)>]
  abstract member FullName : [<MarshalAs(UnmanagedType.BStr)>] string with get

  [<DispId(0x3e8)>]
  abstract member Arguments : [<MarshalAs(UnmanagedType.BStr)>] string with get, set

  [<DispId(0x3ec)>]
  abstract member RelativePath : [<MarshalAs(UnmanagedType.BStr)>] string with set

  [<DispId(0x3ee)>]
  abstract member WindowStyle : int with get, set

  [<DispId(0x3ef)>]
  abstract member WorkingDirectory : [<MarshalAs(UnmanagedType.BStr)>] string with get, set

  [<DispId(0x7d0); TypeLibFunc(0x40s)>]
  abstract member Load : [<MarshalAs(UnmanagedType.BStr)>] PathLink:string -> unit

  [<DispId(0x7d1)>]
  abstract member Save : unit -> unit

如您所见,您不能将属性添加到 F# 中抽象 属性 的基础 getter/setter 方法,只能添加到 属性 本身,但这并不重要这个特定的界面:

  • 具有 getset 的字符串属性无论如何都需要相同的 MarshalAs
  • In is the default directionality for string parameters,所以无论如何指定它都是多余的。
  • 像您的 C# 代码那样将 DispId 应用于 属性 getter 是合法的但毫无意义 – 而 DispId 可以同时应用于方法和属性,并且 属性 getters 和 setter 在技术上恰好是方法,属性只对 属性 本身有影响。

N.b。因为 the CLR marshals string parameters for COM methods as BStrs by default,我们也可以省略所有 MarshalAs 指令,使它看起来更 trim(尽管不那么明确):

[<ComImport; Guid("F935DC23-1CF0-11D0-ADB9-00C04FD58A0B"); TypeLibType(0x1040s)>]
type private IWshShortcut =
  [<DispId(0)>] abstract member FullName:string with get
  [<DispId(0x3e8)>] abstract member Arguments:string with get, set
  [<DispId(0x3ec)>] abstract member RelativePath:string with set
  [<DispId(0x3ee)>] abstract member WindowStyle:int with get, set
  [<DispId(0x3ef)>] abstract member WorkingDirectory:string with get, set
  [<DispId(0x7d0); TypeLibFunc(0x40s)>] abstract member Load : PathLink:string -> unit
  [<DispId(0x7d1)>] abstract member Save : unit -> unit

当然,所有这些也适用于C#实现,因此可以类似地简化:

[ComImport, Guid("F935DC23-1CF0-11D0-ADB9-00C04FD58A0B"), TypeLibType((short)0x1040)]
private interface IWshShortcut
{
    [DispId(0)] string FullName { get; }
    [DispId(0x3e8)] string Arguments { get; set; }
    [DispId(0x3ec)] string RelativePath { set; }
    [DispId(0x3ee)] int WindowStyle { get; set; }
    [DispId(0x3ef)] string WorkingDirectory { get; set; }
    [DispId(0x7d0), TypeLibFunc((short)0x40)] void Load(string PathLink);
    [DispId(0x7d1)] void Save();
}