【问题标题】:Generic array in protocol without generic constraint没有通用约束的协议中的通用数组
【发布时间】:2018-02-09 18:49:16
【问题描述】:

在一致性类型指定数组类型的协议中,如何指定泛型属性?

信息:假设我们要创建一个定义数组的协议 数组类型未知的属性。符合的类型 指定此数组的类型。这是一个可能的解决方案:

protocol Wallet {
    associatedtype Value
    var moneyStack: [Value] {get set}
}

struct BitcoinWallet: Wallet {
    var moneyStack: [String]
}

struct EthereumWallet: Wallet {
    var moneyStack: [Int]
}

这种方法的问题是我们无法访问moneyStack 属性:

let bitcoinWallet = BitcoinWallet(moneyStack: ["A","B"])
let etheureumWallet = EthereumWallet(moneyStack: [1,2])

let wallets: [Wallet] = [bitcoinWallet, etheureumWallet]

for wallet in wallets {
    print(wallet.moneyStack) // not possible
}

我们可以尝试另一种方法,其中数组的类型是任意的:

protocol Wallet {
    var moneyStack: [Any] {get set}
}

struct BitcoinWallet: Wallet {
    var moneyStack: [Any]
}

struct EthereumWallet: Wallet {
    var moneyStack: [Any]
}

这是一个可能的解决方案,但包含 [Any] 的数组也是 通用的。我无法指定类型。示例:

let bitcoinWallet = BitcoinWallet(moneyStack: ["A","B"])
let ethereumWallet = EthereumWallet(moneyStack: [1,2])

var wallets: [Wallet] = [bitcoinWallet, ethereumWallet]

for wallet in wallets {
    print(wallet.moneyStack.count) // this works
    print(wallet.moneyStack[0]) // this works
}

let badBitcoinWallet = BitcoinWallet(moneyStack: [12.4, 312312.123]) // BitcoinWallets aren't allowed to hold doubles!

wallets.append(badBitcoinWallet) // This shouldn't be allowed!

第一个解决方案就是我想要的。每个结构中都指定了类型,但 Swift 抱怨我不能使用泛型约束。我要解决的主要问题是我想要一个包含不同类型钱包的数组,这些钱包都有自己的不同类型的数组。

我认为协议会使这变得简单,但事实并非如此。我的架构做错了吗?我什至应该使用协议吗?也许子类化可以解决这个问题?

【问题讨论】:

  • 我觉得这有点令人不安,但请参阅 Rob Napier 关于此主题的有趣讨论:dotconferences.com/2016/01/…
  • 我能谈谈你对它为什么不舒服的看法吗?我很想听听您的反馈@Rob,这样我就可以看到其他观点。谢谢你的链接!
  • 在类型擦除模式中,所有协议方法的所有闭包(然后您必须为每个符合类型重复)感觉有点笨拙,对于一些莫名其妙的限制来说是一种笨拙的解决方法在语言中(具有 typealias 的协议不能用作类型,而没有的协议则可以)。当你发现自己在重复一些这样的无脑代码时,这是一些更深层次问题的代码异味。 Rob Napier 自己也提到了这些问题,有趣的是,他对通用问题解决者的解决方案最终使用了完全不同的模式,即组合。
  • 非常感谢@Rob!我希望我可以对评论进行投票,但该选项对我来说是不可见的。你说的有道理,我确实必须改变架构。

标签: ios swift generics swift-protocols


【解决方案1】:

您不能在数组中存储 2 个不同类型的对象(Wallet<String>Wallet<Int> 是不同的类型)。

但是你可以试试这样的:

protocol WalletProtocol {
    func printMoneyStack()
}

class Wallet<T> {
    var moneyStack: [T] = []
    init(moneyStack: [T]) {
        self.moneyStack = moneyStack
    }
}

class BitcoinWallet: Wallet<String>, WalletProtocol {
    func printMoneyStack() {
        print(moneyStack)
    }
}

class EthereumWallet: Wallet<Int>, WalletProtocol {
    func printMoneyStack() {
        print(moneyStack)
    }
}


let bitcoinWallet = BitcoinWallet(moneyStack: ["A","B"])
let etheureumWallet = EthereumWallet(moneyStack: [1,2])

let wallets: [WalletProtocol] = [bitcoinWallet, etheureumWallet]

for wallet in wallets {
    wallet.printMoneyStack()
}

【讨论】:

    猜你喜欢
    • 2016-02-18
    • 2020-04-18
    • 1970-01-01
    • 2022-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多