所需接口中要求实现名为“Item”的索引器,但无法实现?

4

我正在尝试实现ESPRIT API中一个类的接口,该接口需要一个名为“Item”的索引器。(我猜这个接口来自VB.NET,但我没有源代码。)显然,“Item[index]”索引器默认情况下由编译器自动生成,但我遇到了以下错误:

Errors

我知道 [System.Runtime.CompilerServices.IndexerName("Item")] 是多余的;它只是为了明确地展示 Item 是被生成的,而错误仍然存在。
尝试实现 public ToolBarControl Item[int index] 会导致类中其他所有内容都崩溃。 编辑1:第三个截图显示了 IToolBar.this,但我已经尝试过使用 IToolBar.Item 并得到了上述结果。 看起来我不是第一个请求命名迭代器的人,但是我认为生成的命名索引器应该满足要求。 https://github.com/dotnet/csharplang/issues/471 如何实现这个索引器以满足接口?

注意:我进行以下编辑是为了完整性、未来的故障排除以及回答评论中提出的问题,但我已经知道这个特定实例的解决方案是按照接受的答案所述实现get_Item(int index)

编辑2:回答“Visual Studio建议什么?”如您所见,在进行替换之前,它知道Index将出错,因为在Item上没有定义Index参数(我已经测试过,确实失败)。自动实现的另外两个选项都不起作用。 Visual Studio suggestion

编辑3:从对象浏览器中,在IToolBar内,Item[int]被定义为:Item definition


1
在移除自己的代码后,当你将鼠标悬停在 Esprit.Tooolbar 上时,Visual Studio 建议的实现是什么? - Parrish Husband
3
你确定 this[] 的返回类型是 'ToolBarControl' 吗? - Neil
如果找不到文档,请在反汇编器中打开引用并查看它具体正在寻找什么。@Neil可能是正确的,它可能期望从索引器中获得“对象”或“IToolbarControl”。 - Parrish Husband
@ParrishHusband,有文档可供参考,但它并未涉及子类化Esprit.Toolbar。请查看问题中添加的附加信息以获取VS建议实现的答案。 - Still.Tony
@Neil 我已经验证了返回类型。请查看对象浏览器中 IToolBarItem[int] 定义的附加编辑和截图。 - Still.Tony
显示剩余2条评论
2个回答

4
C#可以通过接口中的"索引器"和get_Item来实现。这是因为在IL编译期间生成属性/索引器getter和setter的方式。以下是CLI规范中的描述:

I.10.4 命名模式

对于属性:

通过决定getter方法返回的类型和getter参数的类型(如果有)。然后,创建两个基于属性名称和这些类型的方法。在下面的示例中,我们定义了两个属性:Name不需要参数并返回System.String,而Item需要一个System.Object参数并返回System.Object。Item被称为索引属性,意味着它需要参数,因此可以像数组索引一样出现在用户面前。

PropertyGet, used to read the value of the property
   Pattern: <PropType> get_<PropName> (<Indices>)
   Example: System.String get_Name ();
   Example: System.Object get_Item (System.Object key);
PropertySet, used to modify the value of the property
   Pattern: void set_<PropName> (<Indices>, <PropType>)
   Example: void set_Name (System.String name);
   Example: void set_Item (System.Object key, System.Object value); 

因此,您应该能够通过实现一些类似以下内容的索引器来满足条件:
public class ManagedEspritToolbar : Esprit.Toolbar
{
    public ToolbarControl get_Item(int index) => Toolbar[index];
}

为了测试这个,您可以在VB.NET中创建一个简单的界面:

Public Interface IVBNetInterface
    Property Item(index As Integer) As String
End Interface

然后在C#中的一个新类中实现该接口。注意,当允许IDE自动实现接口时,它默认为get_Item/set_Item访问器:

public class CSharpClass : IVBNetInterface
{
    public string get_Item(int index)
    {
        throw new NotImplementedException();
    }

    public void set_Item(int index, string Value)
    {
        throw new NotImplementedException();
    }
}

阅读接口生成的IL代码可以证实这种行为:

enter image description here


VB.NET 的默认属性是什么?

在 VB.NET 中,有一个 Default 属性修饰符,它本质上是在类上声明索引器的机制:

Public Interface IVBNetInterface
    Default Property Item(index As Integer) As String
End Interface

当VB.NET类/接口正确实现时,标准的C# this[int] 索引实现将会起作用。因此,get_Item 的解决方法只有在目标索引属性未正确应用默认属性时才是必要的。请注意,一旦应用了该属性,调查IL代码时会添加System.Reflection.DefaultMemberAttribute 属性。

enter image description here


提高使用效率:

为了规避底层类/接口没有使用Default修饰符的问题,您可以显式实现接口索引器,这样可以在类上公开传统的C#风格的索引器:

public class CSharpClass : IVBNetInterface
{
    public string this[int index]
    {
        get => throw new NotImplementedException();
        set => throw new NotImplementedException();
    }

    #region IVBNetInterface

    string IVBNetInterface.get_Item(int index) => this[index];

    void IVBNetInterface.set_Item(int index, string value) => this[index] = value;

    #endregion
}

如果您希望通过典型索引器推断类的使用方式,同时仍满足基础Interface.Item要求,则这可能是首选方法。

1
我将 get_Item 实现为 public ToolBarControl get_Item(int index) => ToolBar.Item[index];,这个实现(奇怪的是)满足了 Item[int] get 的要求。你想在回答顶部添加一个 TLDR 段落,简单说明 get_Item(int index) 可以满足接口的 Item 要求吗? - Still.Tony
1
@Still.Tony,我曾经使用过另一个用VB.NET编写的第三方库,发现了类似的行为。如果你有一个IL反汇编器,看一下IToolbar的底层代码可能会很有价值。也许你可以在问题中加入“Esprit”,这样其他人就可以找到它了。 - Parrish Husband

0
问题在于,没有有效的C#语法来声明命名索引器,因此您无法使用C#满足该接口。已经有多个请求实现C#中的命名索引器,但所有这些请求都被C#语言团队忽略或直接拒绝了。他们似乎不想在C#中实现此功能。也许如果他们以后改变主意,您可能会有机会。
目前我能想到的唯一方法是创建一个VB.NET库,其中包含一个实现该接口并充当您自己类的包装器或基类的类。

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