草庐IT

Swift:是否可以向协议(protocol)添加协议(protocol)扩展?

coder 2023-07-16 原文

假设我有两个协议(protocol):

protocol TheirPcol {}
protocol MyPcol {
    func extraFunc()
}

我想要做的是为“TheirPcol”创建一个协议(protocol)扩展,让 extraFunc()处理任何符合“TheirPcol”的东西。所以像这样:
extension TheirPcol : MyPcol { // Error 'Extension of protocol 'TheirPcol' cannot have an inheritance clause.
    func extraFunc() { /* do magic */}
}

struct TheirStruct:TheirPcol {}
let inst = TheirStruct()
inst.extraFunc()

这其中的关键是“TheirPcol”、“TheirStruct”都由我无法控制的外部 API 处理。所以我通过了实例“inst”。

这能做到吗?或者我将不得不做这样的事情:
struct TheirStruct:TheirPcol {}
let inst = TheirStruct() as! MyPcol
inst.extraFunc()

最佳答案

似乎有两个用例可以说明为什么您可能想要做您正在做的事情。在第一个用例中,Swift 将允许你做你想做的事,但在第二个用例中不是很干净。我猜你属于第二类,但我会同时进行。

扩展 TheirPcol 的功能

您可能想要这样做的原因之一只是为 TheirPcol 提供额外的功能。 .正如编译器错误所说,您不能扩展 Swift 协议(protocol)以符合其他协议(protocol)。但是,您可以简单地扩展 TheirPcol .

extension TheirPcol {
    func extraFunc() { /* do magic */ }
}

在这里,您将提供符合 TheirPcol 的所有对象。方法extraFunc()并为其提供默认实现。这完成了为符合 TheirPcol 的对象扩展功能的任务。 ,如果您希望它也适用于您自己的对象,那么您可以使您的对象符合 TheirPcol .但是,在许多情况下,您希望保留 MyPcol作为您的主要协议(protocol),只需对待 TheirPcol符合 MyPcol .不幸的是,Swift 目前不支持声明符合其他协议(protocol)的协议(protocol)扩展。

使用 TheirPcol对象就好像它们是 MyPcol
在您确实需要单独存在 MyPcol 的用例(很可能是您的用例)中,然后据我所知,还没有干净的方法可以做你想做的事。以下是一些有效但非理想的解决方案:

wrapper TheirPcol
一种潜在的困惑方法是使用 structclass像下面这样:
struct TheirPcolWrapper<T: TheirPcol>: MyPcol {
    var object: T

    func extraFunc() { /* Do magic using object */ }
}

从理论上讲,当您需要使现有对象实例符合 MyPcol 时,您可以将此结构用作强制转换的替代方法,如您的示例中所示。 .或者,如果您有接受 MyPcol 的函数作为通用参数,您可以创建接受 TheirPcol 的等效函数。 ,然后将其转换为 TheirPcolWrapper并将其发送到另一个函数接收 MyPcol .

另一件要注意的事情是,如果您正在传递一个对象 TheirPcol ,那么您将无法创建 TheirPcolWrapper实例而无需先将其转换为显式类型。这是由于 Swift 的一些泛型限制。因此,像这样的对象可能是另一种选择:
struct TheirPcolWrapper: MyPcol {
    var object: MyPcol

    func extraFunc() { /* Do magic using object */ }
}

这意味着您可以创建一个 TheirPcolWrapper不知道 TheirPcol 的显式类型的实例你得到了。

然而,对于一个大型项目,这两者都会很快变得困惑。

使用子协议(protocol)扩展单个对象

另一个非理想的解决方案是扩展每个您知道符合 TheirPcol 的对象。并且您知道您希望支持。例如,假设您知道 ObjectAObjectB符合 TheirPcol .您可以创建一个 MyPcol 的子协议(protocol)。然后显式声明两个对象的一致性,如下所示:
protocol BridgedToMyPcol: TheirPcol, MyPcol {}

extension BridgedToMyPcol {
    func extraFunc() {
        // Do magic here, given that the object is guaranteed to conform to TheirPcol
    }
}

extension ObjectA: BridgedToMyPcol {}
extension ObjectB: BridgedToMyPcol {}

不幸的是,如果您希望支持大量对象,或者您无法提前知道这些对象是什么,那么这种方法就会失效。当您不知道 TheirPcol 的显式类型时,这也会成为一个问题。你得到了,虽然你可以使用 type(of:)得到一个元类型。

关于 Swift 4 的说明

您应该查看 Conditional conformances ,一个被 Swift 4 接受的提案。具体来说,这个提案概述了具有以下扩展的能力:
extension Array: Equatable where Element: Equatable {
    static func ==(lhs: Array<Element>, rhs: Array<Element>) -> Bool { ... }
}

虽然这不是您要问的,但在底部您会找到“考虑的替代方案”,其中有一个名为“扩展协议(protocol)以符合协议(protocol)”的小节,这更多是您想要做的。它提供了以下示例:
extension Collection: Equatable where Iterator.Element: Equatable {
    static func ==(lhs: Self, rhs: Self) -> Bool {
        // ...
    }
}

然后声明如下:

This protocol extension would make any Collection of Equatable elements Equatable, which is a powerful feature that could be put to good use. Introducing conditional conformances for protocol extensions would exacerbate the problem of overlapping conformances, because it would be unreasonable to say that the existence of the above protocol extension means that no type that conforms to Collection could declare its own conformance to Equatable, conditional or otherwise.



虽然我意识到您并没有要求具有条件一致性的能力,但这是我在讨论扩展协议(protocol)以符合其他协议(protocol)时能找到的最接近的事情。

关于Swift:是否可以向协议(protocol)添加协议(protocol)扩展?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41993616/

有关Swift:是否可以向协议(protocol)添加协议(protocol)扩展?的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby - 我需要将 Bundler 本身添加到 Gemfile 中吗? - 2

    当我使用Bundler时,是否需要在我的Gemfile中将其列为依赖项?毕竟,我的代码中有些地方需要它。例如,当我进行Bundler设置时:require"bundler/setup" 最佳答案 没有。您可以尝试,但首先您必须用鞋带将自己抬离地面。 关于ruby-我需要将Bundler本身添加到Gemfile中吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/4758609/

  3. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  4. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  5. ruby - 我可以使用 Ruby 从 CSV 中删除列吗? - 2

    查看Ruby的CSV库的文档,我非常确定这是可能且简单的。我只需要使用Ruby删除CSV文件的前三列,但我没有成功运行它。 最佳答案 csv_table=CSV.read(file_path_in,:headers=>true)csv_table.delete("header_name")csv_table.to_csv#=>ThenewCSVinstringformat检查CSV::Table文档:http://ruby-doc.org/stdlib-1.9.2/libdoc/csv/rdoc/CSV/Table.html

  6. ruby - 将 Bootstrap Less 添加到 Sinatra - 2

    我有一个ModularSinatra应用程序,我正在尝试将Bootstrap添加到应用程序中。get'/bootstrap/application.css'doless:"bootstrap/bootstrap"end我在views/bootstrap中有所有less文件,包括bootstrap.less。我收到这个错误:Less::ParseErrorat/bootstrap/application.css'reset.less'wasn'tfound.Bootstrap.less的第一行是://CSSReset@import"reset.less";我尝试了所有不同的路径格式,但它

  7. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  8. ruby - 检查数组是否在增加 - 2

    这个问题在这里已经有了答案:Checktoseeifanarrayisalreadysorted?(8个答案)关闭9年前。我只是想知道是否有办法检查数组是否在增加?这是我的解决方案,但我正在寻找更漂亮的方法:n=-1@arr.flatten.each{|e|returnfalseife

  9. ruby - 续集在添加关联时访问many_to_many连接表 - 2

    我正在使用Sequel构建一个愿望list系统。我有一个wishlists和itemstable和一个items_wishlists连接表(该名称是续集选择的名称)。items_wishlists表还有一个用于facebookid的额外列(因此我可以存储opengraph操作),这是一个NOTNULL列。我还有Wishlist和Item具有续集many_to_many关联的模型已建立。Wishlist类也有:selectmany_to_many关联的选项设置为select:[:items.*,:items_wishlists__facebook_action_id].有没有一种方法可以

  10. ruby - 我可以使用 aws-sdk-ruby 在 AWS S3 上使用事务性文件删除/上传吗? - 2

    我发现ActiveRecord::Base.transaction在复杂方法中非常有效。我想知道是否可以在如下事务中从AWSS3上传/删除文件:S3Object.transactiondo#writeintofiles#raiseanexceptionend引发异常后,每个操作都应在S3上回滚。S3Object这可能吗?? 最佳答案 虽然S3API具有批量删除功能,但它不支持事务,因为每个删除操作都可以独立于其他操作成功/失败。该API不提供任何批量上传功能(通过PUT或POST),因此每个上传操作都是通过一个独立的API调用完成的

随机推荐