是否可以使用基于返回值的设置占位符类型?



我想在协议中指定一个返回值与当前实现类的类型匹配的函数。

protocol MakesSelf {
static func getInstance<T>() -> T
}

除非 T 被限制为与实现 MakesSelf 的人相同类型,例如

class MyThing: MakesSelf {
class func getInstance<T>() -> T {
return MyThing()
}
}

我的理解编译器会根据返回值为 T 分配 MyThing 的类型,但我反而得到一个类型转换错误:Cannot convert return expression of type 'MyThing' to return type 'T'

您可以创建一个具有关联类型的协议,其中getInstance()函数将返回该类型。在协议的扩展中创建该函数的默认实现。完整示例:

protocol Initializable {
init()
}
protocol MakesSelf: Initializable {
associatedtype Object: Initializable = Self
static func getInstance() -> Object
}
extension MakesSelf {
static func getInstance() -> Object {
return Object()
}
}
class MyThing: MakesSelf {
required init() {}
func printSelf() {
print(MyThing.self)
print(Object.self)
// Both will print "MyThing" because Object is a typealias of MyThing class
}
}

然后获取您的实例:

let instance = MyThing.getInstance()
print("(instance.self)")
/* Will print: MyTestApp.MyThing */

如您所见,因为您已经在协议扩展中提供了默认的getInstance()实现,这在符合类中是不必要的。在您的问题的上下文中,关联的类型用作"占位符"。

我面临的问题是,在MyThing的情况下无法推断T。编译器在错误消息方面不是很有帮助(有一次我被告知Cannot convert return expression of type 'T' (aka 'MyImplementation') to return type 'T'

解决方案是为编译器提供某种方法来知道T应该是什么(如果您希望它不受约束,请使用AnyObject,而不是T(。

就我而言,我想返回一个 Self 类型的值(实现的类型(。这比正常的 POP 问题更复杂,因为你不能在类/静态函数中引用 Self。

为了解决这个问题,我使用了 Swift 的associatedtype,它允许我们设置一个命名占位符,以便在协议中使用。由于我们在协议级别定义它并提供一个值,因此我们可以设置对实现者自己的类型的命名引用。

associatedtype MyType = Self.Type 

请注意associatedtype MyType = Self不起作用,因为在此上下文中,self是协议,而不是最终实现的类型(我们还不知道,我们/编译器直到某个对象实际实现协议时才知道(。

由于我们提供了一个值,因此当我们实现协议时,类型已经受到限制!由于约束仅引用实现的类型,因此实现协议足以定义类型(cool(。最好的部分是,现在我可以在我的class func定义中消除对自我的引用:

protocol MakesSelf {
associatedtype MyType = Self.Type
static func getInstance() -> MyType
}
class MyImplementation: MakesSelf {
class func getInstance() -> MyImplementation {
print("Hello")
return MyImplementation()
}
}
let myThing = MyImplementation.getInstance() 

在这里要非常清楚 - 类 MyImplementation 有一个名为 MyType 的类型别名,由协议指定。当我编译时,别名将引用当前的实现。现在,当我实现函数getInstance时,MyImplementation符合MyType。

请注意,我必须以class func getInstance() -> MyImplementation而不是class func getInstance() -> MyType的形式编写我的函数签名。这是因为我返回的是函数调用MyImplementation()的结果,这恰好符合MyType的约束。这与说 MyImplementation 可以通过隐式转换为 MyType 不同。

有关尝试为返回 self 制定协议函数规范的良好阅读,请参阅此处,但我想发布具体的解决方案和解释。

最新更新