如何实施IEnumerable<T>
?
假设我有一个我想要实现的课程IEnumerable<T>
:
TStackoverflow<T> = class(TInterfacedObject, IEnumerable<T>)
public
{ IEnumerable<T> }
function GetEnumerator: IEnumerator<T>;
end;
var
IEnumerable<TMouse> mices = TStackoverflow<TMouse>.Create;
我会有一个实现:
TStackoverflow<T> = class(TInterfacedObject, IEnumerable<T>)
public
{ IEnumerable<T> }
function GetEnumerator: IEnumerator<T>;
end;
function TStackoverflow<T>.GetEnumerator: IEnumerator<T>;
begin
Result := {snip, not important here};
end;
现在,为了成为一名优秀的程序员,我会选择我的班级也支持IEnumerable
界面:
TStackoverflow<T> = class(TInterfacedObject, IEnumerable<T>, IEnumerable)
public
{ IEnumerable<T> }
function GetEnumerator: IEnumerator<T>;
{ IEnumerable }
function GetEnumerator: IEnumerator;
end;
function TStackoverflow<T>.GetEnumerator: IEnumerator<T>;
begin
Result := {snip, not important here};
end;
function TStackoverflow.GetEnumerator: IEnumerator;
begin
Result := {snip, not important here};
end;
那些知道这种情况的人会知道实施IEnumerable
是一个红鲱鱼;并且必须以任何一种方式完成。
该代码无法编译,因为:
function GetEnumerator: IEnumerator<T>;
function GetEnumerator: IEnumerator;
我有两个具有相同签名的方法(不是真正相同的签名,但同样足以让Delphi无法区分它们):
E2254重载过程'GetEnumerator'必须标有'overload'指令
好的,我会将它们标记为overloads
:
function GetEnumerator: IEnumerator<T>; overload;
function GetEnumerator: IEnumerator; overload;
但这不起作用:
E2252具有相同参数的方法'GetEnumerator'已存在
重载是错误的方法,我们应该关注method resolution clauses:
type
TStackoverflow<T> = class(TInterfacedObject, IEnumerable<T>, IEnumerable)
protected
function GetEnumeratorTyped: IEnumerator<T>;
function GetEnumeratorGeneric: IEnumerator;
public
function IEnumerable<T>.GetEnumerator = GetEnumeratorTyped;
function IEnumerable.GetEnumerator = GetEnumeratorGeneric;
end;
{ TStackoverflow }
function TStackoverflow<T>.GetEnumeratorGeneric: IEnumerator;
begin
end;
function TStackoverflow<T>.GetEnumeratorTyped: IEnumerator<T>;
begin
end;
除了这个也没有编译,出于逃避我的原因:
E2291缺少接口方法IEnumerable.GetEnumerator
的实现
IEnumerable
假装我不在乎我的班级是否不支持IEnumerable
,请将其作为支持的界面删除。它实际上并没有改变任何内容,因为IEnumerable<T>
来自IEnumerble
:
IEnumerable<T> = interface(IEnumerable)
function GetEnumerator: IEnumerator<T>;
end;
所以方法必须,以及删除IEnumerable
的代码:
type
TStackoverflow<T> = class(TInterfacedObject, IEnumerable<T>)
protected
function GetEnumeratorTyped: IEnumerator<T>;
public
function IEnumerable<T>.GetEnumerator = GetEnumeratorTyped;
end;
{ TStackoverflow }
function TStackoverflow<T>.GetEnumeratorTyped: IEnumerator<T>;
begin
end;
由于同样的原因,无法编译:
E2291缺少接口方法IEnumerable.GetEnumerator
的实现
让我们停下来,合作并倾听。 IEnumerable又回到了一个古老的新发明:
type
TStackoverflow = class(TInterfacedObject, IEnumerable)
public
function GetEnumerator: IEnumerator;
end;
{ TStackoverflow }
function TStackoverflow.GetEnumerator: IEnumerator;
begin
end;
很好,这很有效。我可以得到我的班级支持IEnumerable
。现在我想支持IEnumerable<T>
。
这引出了我的问题:
更新:忘记附加完整的非功能代码:
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
TStackoverflow<T> = class(TInterfacedObject, IEnumerable<T>, IEnumerable)
protected
function GetEnumeratorTyped: IEnumerator<T>;
function GetEnumeratorGeneric: IEnumerator;
public
function IEnumerable<T>.GetEnumerator = GetEnumeratorTyped;
function IEnumerable.GetEnumerator = GetEnumeratorGeneric;
end;
{ TStackoverflow<T> }
function TStackoverflow<T>.GetEnumeratorGeneric: IEnumerator;
begin
end;
function TStackoverflow<T>.GetEnumeratorTyped: IEnumerator<T>;
begin
end;
begin
try
{ TODO -oUser -cConsole Main : Insert code here }
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
答案 0 :(得分:10)
type
TStackoverflow<T> = class(TInterfacedObject, IEnumerable<T>, IEnumerable)
public
{ IEnumerable<T> }
function GetEnumeratorGeneric: IEnumerator<T>;
function IEnumerable<T>.GetEnumerator = GetEnumeratorGeneric;
{ IEnumerable }
function GetEnumerator: IEnumerator;
function IEnumerable.GetEnumerator = GetEnumerator;
end;
您的尝试似乎失败了,因为您的方法都没有被命名为 GetEnumerator
。在我上面的代码中,其中一个名为GetEnumerator
,另一个名称不同。从我的实验中,你必须有一个与接口方法同名的实现方法。
这很奇怪。我说这看起来像编译器错误。该行为甚至持续到XE7。
另一方面,至少你现在有办法向前迈进。
<强>更新强>
好的,Stefan在下面的评论引导我,姗姗来迟地了解到底发生了什么。实际上你需要在这里满足三种接口方法。显然,我们必须为IEnumerable.GetEnumerator
提供实施,以满足IEnumerable
。但由于IEnumerable<T>
来自IEnumerable
,因此IEnumerable<T>
包含两种方法IEnumerable.GetEnumerator
和IEnumerable<T>.GetEnumerator
。所以你真正希望能做的是这样的事情:
我发现有点难以跟踪冲突的名字和泛型,所以我打算提供另一个例子,我认为更清楚地提出关键点:
type
IBase = interface
procedure Foo;
end;
IDerived = interface(IBase)
procedure Bar;
end;
TImplementingClass = class(TInterfacedObject, IBase, IDerived)
procedure Proc1;
procedure IBase.Foo = Proc1;
procedure Proc2;
procedure IDerived.Bar = Proc2;
end;
现在,请注意,使用接口继承意味着IDerived
包含两个方法Foo
和Bar
。
上面的代码不会编译。编译器说:
E2291缺少接口方法IBase.Foo的实现
这当然看起来很奇怪,因为方法解决条款当然处理了这个问题。嗯,不,方法解决方案涉及Foo
中声明的IBase
,而不是IDerived
中继承的TImplementingClass = class(TInterfacedObject, IBase, IDerived)
procedure Proc1;
procedure IBase.Foo = Proc1;
procedure IDerived.Foo = Proc1;
procedure Proc2;
procedure IDerived.Bar = Proc2;
end;
。
在上面的例子中,我们可以很容易地解决问题:
IEnumerable<T>
这足以让编译器满意。我们现在已经为所有需要实施的三种方法提供了实现。
很遗憾,您无法使用IEnumerable<T>
执行此操作,因为继承的方法与{{1}}引入的新方法具有相同的名称。
再次感谢Stefan带领我进入启蒙。
答案 1 :(得分:9)
快速简答
两个接口组具有相同的名称,例如:非通用IEnumerable
和:IEnumerable<T>
。
<强>概述强>
遇到同样的问题。
问题不在于接口实现或通用接口实现。
通用接口(如IEnumerable<T>
)存在特殊情况,因为它们还具有非通用IEnumerable
接口。这个接口是为了与O.S.兼容,它们不仅仅是另一个编程接口。
此外,还有一些其他通用和非通用父接口,如IEnumerator
或IComparable
,它们都有自己的成员定义。
<强>解决方案强>
为了解决同样的问题,我会注意哪些父接口IEnumerable<T>
具有,并检测匹配的非通用接口。
并且,实现了非泛型接口的添加和中间非泛型类。这使我可以轻松地检测出哪些接口和接口。匹配成员定义缺失。
type
// Important: This intermediate class declaration is NOT generic
// Verify that non generic interface members implemented
TStackoverflowBase = class(TInterfacedObject, IEnumerable)
protected
function GetEnumeratorGeneric: IEnumerator;
public
function IEnumerable.GetEnumerator = GetEnumeratorGeneric;
end;
// Important: This proposed class declaration IS generic,
// yet, it descends from a non generic intermediate class.
// After verify that non generic interface members implemented,
// verify that generic interface members implemented,
TStackoverflow<T> = class(TStackoverflowBase, IEnumerable<T>)
protected
function GetEnumeratorTyped: IEnumerator<T>;
public
function IEnumerable<T>.GetEnumerator = GetEnumeratorTyped;
end;
事情,更难,因为有几个成员重载了相同的标识符,但不同的参数类型或不同的返回类型, 取决于它们是来自通用接口还是来自非通用接口。
@David Heffernan只有一个类的解决方案,并不坏,我确实尝试了相同的方法,但它更加复杂。
因此,我应用了中间类,因为它允许我找出哪个接口和&amp;各个成员,失踪。
干杯。
答案 2 :(得分:0)
实际上,它很简单,虽然不是很明显。关键是要知道编译器对枚举器有一些魔力。
首先,我们根本不需要实施IEnumerator
或IEnumerator<T>
。相反,我们声明或拥有接口。为了这个示例,我们列举了WideStrings,因此它可能如下所示。请注意,方法/属性名称和签名在此非常重要:
IFoobarEnumerator = interface
function GetCurrent: WideString;
function MoveNext: Boolean;
procedure Reset;
property Current: WideString read GetCurrent;
end;
现在,周围的类得到一个名为GetEnumerator
的方法,它返回一个看起来像枚举器的东西。
我刻意写了&#34; 看起来像一个枚举者的东西&#34;而不是&#34;实现IEnumerator<T>
&#34;的东西,因为那&#39 ;所有编译器需要让魔法发生:
function GetEnumerator : IFoobarEnumerator;
除了我们当然仍然需要为IFoobarEnumerator
实现枚举器类这一事实之外 - 这就是它。我们已经完成了。