問題を行う際
func complete(with models: [Model]) {
doSomethingWithHashable(models) //can't do this
}
func doSomethingWithHashable <T:Hashable>(_ objects: [T]) {
//
}
私は私が得るので、この
protocol Model: Hashable {}
func complete<T:Model>(with models: [T]) {
runComparison(models)
}
を避けるためにしようとしている「モデルは...一般的な制約として使用することはできません」あなたのコードでは、Model
という言葉で話していることです。はありません。約Hashable
適合です。あなたが指摘しているように、これについてコンパイラに伝えること(すなわち、Model
をHashable
から派生させること)は、Model
に準拠する異種のタイプに関して話す能力を失うことになります。
あなたも、あなただけの完全に任意Hashable
準拠したインスタンスのための標準ライブラリのAnyHashable
種類消去されたラッパーを使用することができ、最初の場所では約Model
適合性を気にしない場合。
しかし、あなたはおよそModel
適合性を気にしないと仮定すると、あなたはModel
とHashable
の両方に準拠してインスタンスにtype-erased wrapperあなた自身を構築する必要があります。 my answer hereでは、Equatable
適合タイプのためにどのようにタイプ消しゴムを組み立てることができるかを実証します。ロジックは非常に簡単に拡張することができますHashable
- 我々はちょうどインスタンスのhashValue
を返すために特別な関数を格納する必要があります。
struct AnyHashableModel : Model, Hashable {
static func ==(lhs: AnyHashableModel, rhs: AnyHashableModel) -> Bool {
// forward to both lhs's and rhs's _isEqual in order to determine equality.
// the reason that both must be called is to preserve symmetry for when a
// superclass is being compared with a subclass.
// if you know you're always working with value types, you can omit one of them.
return lhs._isEqual(rhs) || rhs._isEqual(lhs)
}
private let base: Model
private let _isEqual: (_ to: AnyHashableModel) -> Bool
private let _hashValue:() -> Int
init<T : Model>(_ base: T) where T : Hashable {
self.base = base
_isEqual = {
// attempt to cast the passed instance to the concrete type that
// AnyHashableModel was initialised with, returning the result of that
// type's == implementation, or false otherwise.
if let other = $0.base as? T {
return base == other
} else {
return false
}
}
// simply assign a closure that captures base and returns its hashValue
_hashValue = { base.hashValue }
}
var hashValue: Int { return _hashValue() }
}
あなたがそのようにようにそれを使用します:たとえば
私はあなたにもModel
年代のラッパーとしてそれを使用したいと思うことを仮定していここ
func complete(with models: [AnyHashableModel]) {
doSomethingWithHashable(models)
}
func doSomethingWithHashable<T : Hashable>(_ objects: [T]) {
//
}
let models = [AnyHashableModel(Contact()), AnyHashableModel(Address())]
complete(with: models)
要件(いくつかあると仮定)。また、あなたがbase
プロパティを公開し、AnyHashableModel
自体からModel
適合を削除し、発信者が基礎となるModel
準拠したインスタンスのbase
にアクセスすることができます。
struct AnyHashableModel : Hashable {
// ...
let base: Model
// ...
}
ただし上記のタイプが消去されたことをラッパーに注意しますHashable
とModel
の両方にのみ適用されます。準拠しているインスタンスがHashable
である他のプロトコルについて話したいのですが?
もっと一般的な解決法は、in this Q&Aを実証すると、代わりに、Hashable
の両方のタイプを受け入れ、そのタイプは汎用プレースホルダによって表されます。
現在のところ、Swiftには、別の汎用プレースホルダーによって与えられたプロトコルに準拠した汎用プレースホルダーを表現する方法がないため、この関係は、必要なアップキャストを実行するためにtransform
クロージャを使用して発信者が定義する必要があります。しかし、Swift 3.1が拡張で具体的な同じタイプの要件を受け入れることで、Model
のこの定型文を削除する便利な初期設定を定義できます(これは他のプロトコルタイプに対しても繰り返すことができます)。例えば
:
/// Type-erased wrapper for a type that conforms to Hashable,
/// but inherits from/conforms to a type T that doesn't necessarily require
/// Hashable conformance. In almost all cases, T should be a protocol type.
struct AnySpecificHashable<T> : Hashable {
static func ==(lhs: AnySpecificHashable, rhs: AnySpecificHashable) -> Bool {
return lhs._isEqual(rhs) || rhs._isEqual(lhs)
}
let base: T
private let _isEqual: (_ to: AnySpecificHashable) -> Bool
private let _hashValue:() -> Int
init<U : Hashable>(_ base: U, upcast: (U) -> T) {
self.base = upcast(base)
_isEqual = {
if let other = $0.base as? U {
return base == other
} else {
return false
}
}
_hashValue = { base.hashValue }
}
var hashValue: Int { return _hashValue() }
}
// extension for convenience initialiser for when T is Model.
extension AnySpecificHashable where T == Model {
init<U : Model>(_ base: U) where U : Hashable {
self.init(base, upcast: { $0 })
}
}
あなたは今AnySpecificHashable<Model>
であなたのインスタンスをラップしたい:
何 `Model`に準拠して、別のタイプがある場合は、ではなく、`ハッシュ可能に
func complete(with models: [AnySpecificHashable<Model>]) {
doSomethingWithHashable(models)
}
func doSomethingWithHashable<T : Hashable>(_ objects: [T]) {
//
}
let models: [AnySpecificHashable<Model>] = [
AnySpecificHashable(Contact()),
AnySpecificHashable(Address())
]
complete(with: models)
'? –
私はそのシナリオを持っていません – joels
多少の関連性:[Equatableを実装している構造体の配列の操作](http://stackoverflow.com/q/41298464/2976878) - 'AnyHashableModel'型の消去されたラッパーをそのQ&Aの 'AnyVehicle'と同様の方法です(主な違いは、単に' hashValue'取得のために余分な関数を格納することです)。 – Hamish