Ian*_*oyd 16 delphi generics delphi-xe6
我该如何实施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;
Run Code Online (Sandbox Code Playgroud)
我会有一个实现:
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;
Run Code Online (Sandbox Code Playgroud)
现在,为了成为一名优秀的程序员,我会选择我的班级也支持这个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;
Run Code Online (Sandbox Code Playgroud)
那些知道这是怎么回事的人会知道实施IEnumerable是一个红鲱鱼; 并且必须以任何一种方式完成.
该代码无法编译,因为:
function GetEnumerator: IEnumerator<T>;
function GetEnumerator: IEnumerator;
Run Code Online (Sandbox Code Playgroud)
我有两个具有相同签名的方法(不是真正相同的签名,但同样足以让Delphi无法区分它们):
E2254重载过程'GetEnumerator'必须用'overload'指令标记
好的,我会把它们标记为overloads:
function GetEnumerator: IEnumerator<T>; overload;
function GetEnumerator: IEnumerator; overload;
Run Code Online (Sandbox Code Playgroud)
但这不起作用:
E2252具有相同参数的方法'GetEnumerator'已存在
重载是错误的方法,我们应该寻找方法解决条款:
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;
Run Code Online (Sandbox Code Playgroud)
由于这不能编译,出于逃避我的原因:
E2291缺少接口方法IEnumerable.GetEnumerator的实现
IEnumerable假装我不在乎我的班级不支持IEnumerable,让我们把它作为支持的界面删除.它实际上并没有改变任何东西,因为它IEnumerable<T>来自IEnumerble:
IEnumerable<T> = interface(IEnumerable)
function GetEnumerator: IEnumerator<T>;
end;
Run Code Online (Sandbox Code Playgroud)
所以该方法必须存在,并删除代码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;
Run Code Online (Sandbox Code Playgroud)
由于同样的原因不编译:
E2291缺少接口方法IEnumerable.GetEnumerator的实现
所以让我们停下来,合作并倾听.IEnumerable又回来了一个古老的新发明:
type
TStackoverflow = class(TInterfacedObject, IEnumerable)
public
function GetEnumerator: IEnumerator;
end;
{ TStackoverflow }
function TStackoverflow.GetEnumerator: IEnumerator;
begin
end;
Run Code Online (Sandbox Code Playgroud)
非常好,有效.我可以得到我的班级支持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.
Run Code Online (Sandbox Code Playgroud)
Dav*_*nan 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;
Run Code Online (Sandbox Code Playgroud)
您的尝试似乎失败了,因为您的方法都没有被命名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;
Run Code Online (Sandbox Code Playgroud)
现在,请注意,使用接口继承意味着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;
Run Code Online (Sandbox Code Playgroud)
这足以让编译器满意.我们现在已经为所有需要实现的三种方法提供了实现.
遗憾的是,您不能这样做,IEnumerable<T>因为继承的方法与引入的新方法具有相同的名称IEnumerable<T>.
再次感谢Stefan带领我进行启蒙.
快速简答
有两个名称相同的接口组,例如:非泛型IEnumerable和:IEnumerable<T>.
概观
有同样的问题.
问题不在于接口实现或通用接口实现.
通用接口有一种特殊情况IEnumerable<T>,因为它们也有非通用IEnumerable接口.此接口用于与OS兼容,它们不仅仅是另一个编程接口.
并且,还有一些其他通用和非通用父接口,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;
Run Code Online (Sandbox Code Playgroud)
事情,更难,因为有几个成员重载相同的标识符,但不同的参数类型或不同的返回类型,取决于,如果它们来自通用接口或非通用接口.
@David Heffernan只有一个类的解决方案也不错,我确实尝试了相同的方法,但它更加复杂.
因此,我应用了中间类,因为它允许我找出哪些接口和各个成员丢失的地方.
干杯.
| 归档时间: |
|
| 查看次数: |
3585 次 |
| 最近记录: |