如何强制可以从 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 作为通用约束,这没问题。