如何使用F#中的属性来获取和设置接口属性

时间:2016-07-19 21:55:04

标签: f# com c#-to-f#

如何将以下COM接口转换为F#?我无法弄清楚如何注释属性的getset

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

[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();
}

1 个答案:

答案 0 :(得分:6)

这是一个正确的,但不是字面意义的翻译:

[<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,所以指明它无论如何都是多余的。
  • DispId应用于属性getter,因为您的C#代码是合法但无意义的 - 而DispId可以应用于方法和属性,而属性getter和setter在技术上恰好是方法,属性只对属性本身有影响。

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

[<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();
}