如何强制可以从 Swift 中的字符串解析作为参数传入的 class?
How do I enforce that a class passed in as a parameter can be parsed from a String in Swift?
我正在尝试表示一个函数调用,以便我可以编写一些用于创建游戏的脚本语言。现在我只是在尝试设置所有协议和我需要的 classes 之间的接口。我有一个class,FunctionCall<T>
。它有一个执行当前函数的方法 execute()
和 return 类型 T?
的一个实例。 FunctionCall
也有一个 FunctionCall
类型的实例数组来表示任何参数。它还有一个字段 stringRepresentation
,它是用户将输入的函数调用的字符串表示形式。该字符串可能类似于 createNode(named: myCircle)
,或者在基本情况下,它可能只是一个文字,例如 myCircle
。需要明确的是,myCircle
在这种情况下是 String
。在我的小脚本语言中,我可能会选择不在 String
周围使用引号。
我的问题是 execute()
方法。我想要 return 类型 T
的实例。到目前为止,我已经考虑过强制 T
符合一个协议(我们称之为 Parseable
),该协议强制它有一个采用 String
和 return 的方法s 类型 T
的实例。我发现这种方法的问题是我无法创建这样的方法,因为我没有办法从协议中引用将实现协议的类型。换句话说,如果 T 是 SKShapeNode
,我没有办法或从 Parseable
中引用 SKShapeNode
,所以我可以指示 return 类型必须是 SKShapeNode
。我发现的另一种方法是让 Parseable
有一个需要 String
的初始值设定项。这在结构实现协议时有效,但不适用于 classes。当我尝试在 class 中实现协议时遇到的问题是 class 要我创建初始化程序 required
,但我做不到,因为我做不到在扩展中放置一个 required
初始值设定项。
我希望 FunctionCall
class 看起来像这样
class FunctionCall<T: Parseable> {
var parameters = [FunctionCall]()
var stringRepresentation: String!
init(stringRepresentation: String) {
self.stringRepresentation = stringRepresentation
}
func execute() -> T? {
guard let indexOfFirstLeftParen = stringRepresentation.firstIndex(of: "("),
let indexOfLastRightParen = stringRepresentation.lastIndex(of: ")") else {
// then this is a literal value, because no function is being called
return T(string: stringRepresentation)
}
// TODO: implement
return nil
}
}
The problem I found with that approach is that I have no way of creating such a method, because I don't have a way to reference the type that will be implementing the protocol from within the protocol.
实际上,这个问题可以通过使用 Self
来解决,它指的是任何符合类型:
// I think this is a better name
protocol ConvertibleFromString {
static func from(string: String) -> Self?
}
// implementation
extension Int : ConvertibleFromString {
static func from(string: String) -> Int? {
return Int(string)
}
}
对于非 final 类,您必须像这样创建一个 final 子类:
final class SKSpriteNodeFinal : SKSpriteNode, ConvertibleFromString {
static func from(string: String) -> SKSpriteNodeFinal? {
...
}
}
请注意,这会阻止您的协议被用作变量类型:
var foo: ConvertibleFromString? = nil // error
但我认为这不会成为问题,因为您只使用 ConvertibleFromString
作为通用约束,这没问题。
我正在尝试表示一个函数调用,以便我可以编写一些用于创建游戏的脚本语言。现在我只是在尝试设置所有协议和我需要的 classes 之间的接口。我有一个class,FunctionCall<T>
。它有一个执行当前函数的方法 execute()
和 return 类型 T?
的一个实例。 FunctionCall
也有一个 FunctionCall
类型的实例数组来表示任何参数。它还有一个字段 stringRepresentation
,它是用户将输入的函数调用的字符串表示形式。该字符串可能类似于 createNode(named: myCircle)
,或者在基本情况下,它可能只是一个文字,例如 myCircle
。需要明确的是,myCircle
在这种情况下是 String
。在我的小脚本语言中,我可能会选择不在 String
周围使用引号。
我的问题是 execute()
方法。我想要 return 类型 T
的实例。到目前为止,我已经考虑过强制 T
符合一个协议(我们称之为 Parseable
),该协议强制它有一个采用 String
和 return 的方法s 类型 T
的实例。我发现这种方法的问题是我无法创建这样的方法,因为我没有办法从协议中引用将实现协议的类型。换句话说,如果 T 是 SKShapeNode
,我没有办法或从 Parseable
中引用 SKShapeNode
,所以我可以指示 return 类型必须是 SKShapeNode
。我发现的另一种方法是让 Parseable
有一个需要 String
的初始值设定项。这在结构实现协议时有效,但不适用于 classes。当我尝试在 class 中实现协议时遇到的问题是 class 要我创建初始化程序 required
,但我做不到,因为我做不到在扩展中放置一个 required
初始值设定项。
我希望 FunctionCall
class 看起来像这样
class FunctionCall<T: Parseable> {
var parameters = [FunctionCall]()
var stringRepresentation: String!
init(stringRepresentation: String) {
self.stringRepresentation = stringRepresentation
}
func execute() -> T? {
guard let indexOfFirstLeftParen = stringRepresentation.firstIndex(of: "("),
let indexOfLastRightParen = stringRepresentation.lastIndex(of: ")") else {
// then this is a literal value, because no function is being called
return T(string: stringRepresentation)
}
// TODO: implement
return nil
}
}
The problem I found with that approach is that I have no way of creating such a method, because I don't have a way to reference the type that will be implementing the protocol from within the protocol.
实际上,这个问题可以通过使用 Self
来解决,它指的是任何符合类型:
// I think this is a better name
protocol ConvertibleFromString {
static func from(string: String) -> Self?
}
// implementation
extension Int : ConvertibleFromString {
static func from(string: String) -> Int? {
return Int(string)
}
}
对于非 final 类,您必须像这样创建一个 final 子类:
final class SKSpriteNodeFinal : SKSpriteNode, ConvertibleFromString {
static func from(string: String) -> SKSpriteNodeFinal? {
...
}
}
请注意,这会阻止您的协议被用作变量类型:
var foo: ConvertibleFromString? = nil // error
但我认为这不会成为问题,因为您只使用 ConvertibleFromString
作为通用约束,这没问题。