【问题标题】:Swift Generics: Constrain Type Parameter to ProtocolSwift 泛型:将类型参数约束到协议
【发布时间】:2017-12-10 21:12:23
【问题描述】:

如何指定泛型类型参数只能是协议(或符合该协议的协议),而不是符合该协议的类?

例如:

import Foundation

@objc protocol MyProtocol {
    var name: String { get }
}

@objc protocol MySubProtocol: MyProtocol {
    func foo()
}

class MyImplementation: MySubProtocol {
    var name: String

    init(name: String) {
        self.name = name
    }

    func foo() {
        print("Foo! Name: \(self.name)")
    }
}

class InterfaceMaker<T: MyProtocol> {
    init() {}

    func makeInterface() -> NSXPCInterface {
        return NSXPCInterface(with: T.self) // Cannot convert value of type 'T.Type' to expected argument type 'Protocol'
    }


    func getProxy() -> T? {
        // Some magic in here involving `NSXPCConnection.remoteObjectProxy`
    }
}

InterfaceMaker<MySubProtocol>().makeInterface() // No error, as expected
InterfaceMaker<MyImplementation>().makeInterface() // No error, but should not work!

如何指定泛型类型参数应该是协议?

我想将T 限制为仅协议 符合MyProtocol(例如MySubProtocol)。但问题是,我不知道如何防止T成为一个类(比如MyImplementation)。

我已经尝试过约束T.self(我尝试使用where T.self : Protocol,但导致错误'self' is not a member type of 'T')。

那么我如何指定T 必须是一个符合MyProtocol 的协议,而不是一个类如果这不可能,我至少可以指定那 T 应该是 any 协议吗?如果我需要创建class-only protocol 也可以。

MySubProtocol 作为非通用参数传递不是我正在寻找的,因为我还希望能够将该协议用作InterfaceMaker.getProxy() 函数的类型。此外,让该函数简单地返回 MyProtocol(换句话说,让 InterfaceMaker 成为非泛型)也不是一种选择。

注意:要完全清楚,我需要T 作为协议的原因是我要将它传递给NSXPCInterface.init(with:),它需要Protocol(如果SomeProtocol@objc,则可以通过SomeProtocol.self 获得)。这意味着SomeProtocol.self.Type 是或符合

如果这是不可能的,请给出完整的解释原因。还要提及是否有可能在未来的 Swift 版本中支持这一点。

编辑:另一种表述方式是T.self is AnyObject.Type 永远不应该为真。我宁愿在编译时检查这一点,而不是在运行时检查或断言。

【问题讨论】:

  • 你为什么要把 T 限制在协议上?为什么我不能使用超类?
  • @Alexander 你是什么意思?为什么不能使用超类来做什么?
  • 如果我使用的是类继承层次结构(例如CarSportsCar: CarFamilyCar: Car),而不是协议层次结构(例如CarProtocolSportsCarProtocol: CarProtocol、@987654350 @)。为什么要禁止我使用Car 作为接口,而强制我使用CarProtocol
  • @Alexander 因为NSXPCInterface.init(with:) 需要Protocol,正如我在问题中所写。
  • 哦,我错过了。

标签: swift generics type-parameter generic-constraints


【解决方案1】:

您可以检查T 是否符合所有类隐式符合的协议AnyObject,但不符合协议。

所以,在InterfaceMaker

class InterfaceMaker<T: MyProtocol> {
    init() {}

    func makeInterface() -> NSXPCInterface {
        if T.self is AnyObject.Type {
           //  T is a class
        } else {
           return NSXPCInterface(with: T)
        }
    }


    func getProxy() -> T? {
        // Some magic in here involving `NSXPCConnection.remoteObjectProxy`
    }
}

【讨论】:

  • 如果我误解了,请纠正我,但我并不是要让协议本身通用,而只是能够将协议用作类型参数(并将该类型参数限制为仅协议)。
  • 哦,对不起,我误解了你的意思。我会重写答案。
  • 我改变了答案。我希望这是你想要做的
  • 谢谢,这主要是我想要做的。我自己已经做到了这一点。然而,问题在于我希望T.self is AnyObject.Type 永远不可能,可能是通过一般约束。如果这样的限制是不可能的,请解释原因或引用解释原因的来源。
  • 你的意思是在编译时?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-01-31
  • 2020-10-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多