delphi – 如何实现IEnumerable?
如何实现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无法区分它们):
好的,我会把它们标记为超载: function GetEnumerator: IEnumerator<T>; overload; function GetEnumerator: IEnumerator; overload; 但这不行:
界面方法分辨率 超载是错误的做法,我们应该期待method resolution clauses: type TStackoverflow<T> = class(TInterfacedObject,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; 除此之外,还没有编译,因为逃避我的原因:
所以让我们忘记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; 不会因为同样的原因编译:
那好吧,忘了仿制药 所以让我们停止,协作和听. 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) 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. 解决方法
method resolution clause可以做这个工作:
type TStackoverflow<T> = class(TInterfacedObject,IEnumerable) public { IEnumerable<T> } function GetEnumeratorGeneric: IEnumerator<T>; function IEnumerable<T>.GetEnumerator = GetEnumeratorGeneric; { IEnumerable } function GetEnumerator: IEnumerator; function IEnumerable.GetEnumerator = GetEnumerator; end; 您的尝试失败,似乎是因为您的方法都没有被命名为GetEnumerator.在我的代码中,其中一个被称为GetEnumerator,另一个被赋予一个不同的名字.从我的实验中,你必须有一个与接口方法具有相同名称的实现方法. 哪个是奇怪的我会说这看起来像一个编译器的bug.行为仍然持续到XE7. 另一方面,至少你有一个方法可以向前迈进. 更新 好的,Stefan在下面的评论导致我迟来了解真正发生了什么.您实际上需要在这里满足三种接口方法.显然,我们必须为IEnumerable.GetEnumerator提供一个实现,以满足IEnumerable.但是由于IEnumerable< T>来自IEnumerable,然后IEnumerable< T>包含两种方法:IEnumerable.GetEnumerator和IEnumerable> .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. 上面的代码将无法编译.编译器说:
当然,这似乎是奇怪的,因为方法解决条款肯定是这样解决的.那么不,方法决议涉及到在IBase中声明的Foo,而不是继承的IDerived中的Foo. 在上面的例子中,我们可以很容易地解决这个问题: TImplementingClass = class(TInterfacedObject,IDerived) procedure Proc1; procedure IBase.Foo = Proc1; procedure IDerived.Foo = Proc1; procedure Proc2; procedure IDerived.Bar = Proc2; end; 这足以使编译器快乐.我们现在为所有需要实现的三种方法提供了实现. 不幸的是,您无法使用IEnumerable< T>因为继承方法与IEnumerable< T>引入的新方法具有相同的名称. 再次感谢Stefan带领我启迪. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |