接口需要名为 "Item" 的索引器但无法实现?

Indexer named "Item" required by interface but not possible to implement?

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

我意识到 [System.Runtime.CompilerServices.IndexerName("Item")] 是多余的;它只是为了明确地证明 Item 已生成并且错误仍然存​​在。

尝试实施 public ToolBarControl Item[int index] 会破坏 class 其余部分的所有内容。

Edit1:第三个屏幕截图显示 IToolBar.this 但我确实尝试了 IToolBar.Item 并得到了上述结果。

看起来我不是第一个请求命名迭代器的人,但我认为生成的命名索引器应该满足要求。 https://github.com/dotnet/csharplang/issues/471

如何实现这个索引器来满足接口?


注意:为了完整性、未来的故障排除和回答评论中提出的问题,我正在进行以下编辑,但我已经知道针对此特定实例的解决方案正在实施 get_Item(int index),如前所述在接受的答案中。

Edit2:回答"what does visual studio suggest?" 如您所见,它在进行替换之前就知道Index 会出错,因为[=] 上没有定义Index 参数12=](我已经测试过了,它确实失败了。)其他两个自动实现选项都不起作用。

Edit3:在对象浏览器中,IToolBarItem[int] 定义为:

问题是没有用于声明命名索引器的有效 C# 语法,因此您无法使用 C# 满足该接口。有几个要求在 C# 中实现命名索引器的请求都被 C# 语言团队忽略或完全拒绝。他们似乎不想在 C# 中实现此功能。也许如果他们以后改变主意,你可能会得到机会。

目前我能想到的解决这个问题的唯一方法是创建一个 VB.NET 库,其中 class 实现接口并充当包装器或基础 class为你自己 class.

C# 可以满足 get_Item 接口中的 .Item 'indexer'。这是因为在 IL 编译期间如何生成 Property/Index getter 和 setter。

CLI Specification 中是这样描述的:

I.10.4 Naming patterns

For Properties:

An individual property is created by deciding on the type returned by its getter method and the types of the getter’s parameters (if any). Then, two methods are created with names based on the name of the property and these types. For the examples below we define two properties: Name takes no parameters and returns a System.String, while Item takes a System.Object parameter and returns a System.Object. Item is referred to as an indexed property, meaning that it takes parameters and thus can appear to the user as through it were an array with indices.

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# 在一个新的 class 上实现接口。请注意当允许 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 确认此行为:


VB.NET 的默认 属性 怎么样?

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

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

当这在 VB.NET class/interface 上正确实现时,标准 C# this[int] 索引实现将起作用。因此,get_Item 解决方法 只有当默认属性 被正确应用于目标索引 属性.请注意,在应用此属性后调查 IL 代码时会添加 System.Reflection.DefaultMemberAttribute 属性:


改进用法:

为了绕过底层 classes/interfaces not 正在使用 Default 修饰符编写,您可以显式实现接口索引器,这允许公开传统的 C# class:

上的样式化索引器
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
}

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