如何在使用显式接口实现时选择要导出到COM的成员?

3
如果我的类实现了 IEnumerable 接口,我就可以使用 VBScript 的 For Each 循环:
[ComVisible(true)]
[ProgId("Stackoverflow.MyIssues")]
[Guid("7D392CB1-9080-49D0-B9CE-05B214B2C448")]
public class MyIssue : IEnumerable
{
  readonly List<string> issues = new List<string>(new string[] { "foo", "bar" });

  public string this[int index]
  {
    get { return issues[index]; }
  }

  public IEnumerator GetEnumerator()
  {
    return issues.GetEnumerator();
  }
}


Dim o : Set o = CreateObject("Stackoverflow.MyIssues")

Dim i
For Each i In o
  WScript.Echo i
Next

如果我将接口更改为 IEnumerable<string>(这样C#的 foreach 循环就使用 string 而不是 object):
public class MyIssue : IEnumerable<string>

GetEnumerator替换为:

public IEnumerator<string> GetEnumerator()
{
  return issues.GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
  return GetEnumerator();
}

脚本执行时会出现错误:

对象不支持此属性或方法

我的理解是,公共的 GetEnumerator() 方法未被导出,因为它是一个泛型方法,而 IEnumerable.GetEnumerator 也未被导出,因为必须先将实例转换为 IEnumerable,但在 VBScript 中无法转换对象。
这是真的吗? 是否有可能告诉编译器应该将 IEnumerable.GetEnumerator 导出为 public IEnumerable GetEnumerator?(或者这个说法没有意义?等等)

脚本语言仅支持默认接口,不提供查询其他接口的方法。除非您提供帮助。试图公开通用接口是没有意义的,因为它永远不会被使用。 - Hans Passant
由于类型库(http://pastebin.com/YiEwE3yT)在将接口从“IEnumerable”更改为“IEnumerable<string>”时没有更改,因此我天真地认为这不会有影响。 - Micha Wiedenmann
我认为,本质上我是将Mapper.ToEnum移动到了我的类中,并将其重命名为IEnumerable.GetEnumerator()。现在,正如您所解释的那样,IEnumerable.GetEnumerator()无法被调用。因此,我问道:是否可以使GetEnumerator()(或新创建的某些东西)从COM中可调用。对于我理解缓慢,我深感抱歉,并感谢您迄今为止提供的解释! - Micha Wiedenmann
1个回答

2

切换显式实现的接口,例如:

IEnumerator<string> IEnumerable<string>.GetEnumerator()
{
    // ...
}

public IEnumerator GetEnumerator()
{
    // ...
}

这是因为IEnumerable.GetEnumerator带有DispId(-4)属性,这是VBScript通过IDispatch.Invoke使用的。
泛型方法和带有泛型类型的方法不可见于COM,所以如果您不想改变您的代码,请为COM定义额外的方法:
// Test if the .NET framework dispatches the method if you apply the following attribute
//[ComVisible(false)]
[DispId(-4)]
public IEnumerator NewEnum()
{
    return ((IEnumerable)this).GetEnumerator();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接