【问题标题】:Class casting dynamically in swift快速动态地进行类转换
【发布时间】:2023-03-27 09:23:01
【问题描述】:

我正在尝试在 Swift 中动态转换为一个类。这可能吗?这是我尝试使用的代码:

let stringClass: AnyClass = NSString.self
let anyObject: AnyObject = "foo"
let string = anyObject as! stringClass

代码在强制转换时无法编译。这可能吗?如果可以,为什么是正确的语法?

实际用例

这是真正的问题。我正在尝试重构此代码:

switch (value) {
    case "valueOne":
        viewController = storyboard.instantiateViewController(withIdentifier: "foo") as! FirstViewController
    case "valueTwo":
        viewController = storyboard.instantiateViewController(withIdentifier: "bar") as! SecondViewController
    default:
        return nil
}

进入:

let controllersDictionary: [String: (String, UIViewController.Type)] = [
    "valueOne" : ("bar", FirstViewController.self),
    "valueTwo" : ("foo", SecondViewController.self)
]
let tuple = controllersDictionary[value]!
let identifier = tuple.0
let cast = tuple.1
let viewController = storyboard.instantiateViewController(withIdentifier: identifier) as! cast

【问题讨论】:

  • 动态转换在静态类型系统中是否有意义?
  • 是的!在这种情况下,我们的目标是在特定条件下转换为不同的类型(考虑从故事板实例化多个不同的 ViewController)
  • 这个问题通常使用关联类型或泛型来解决。
  • “想想从情节提要中实例化多个不同的 ViewControllers” 我的想象力失败了。您能否展示那个代码,以便我们了解真正的实际问题是什么?
  • @AlexCurran 我仍然不明白你为什么在那一刻需要演员表。为什么标识符不够? viewController 实际上将自动成为右视图控制器子类的实例,那么强制转换是为了什么?

标签: swift casting


【解决方案1】:

我不确定您想要实现的具体目标,但这是您示例的工作版本:

func cast<T>(value: Any, to type: T) -> T? {
    return castedValue as? T
}

let inputValue: Any = "this is a test"
let inputType = String.self()
let casted = cast(value: inputValue, to: inputType)

print(casted)

【讨论】:

  • 这个问题是它与inputType的静态类型一起工作(因此你可以直接进行条件转换)——如果你写let inputType : Any = Int(),那么cast函数将仍然成功。尽管正如您在上面(和下面)评论中正确所说的那样,这种转换在静态类型系统中并没有太大意义。
  • @Hamish 是的。这些都没有意义:p
  • 嗯好的。那么从类结构上来说,s inputType是什么类呢?
  • 这确实有道理,仅仅因为你有静态类型并不意味着你总是知道类型!我的示例在 Java 中是可行的,我想了解在 Swift 中是否可行,如果不可行,为什么。
  • 您可以选择单击标识符以查看有关它的更多信息,包括其(推断或显式)类型
【解决方案2】:

我现在还没有看到演员表的用途。你可以写:

let controllersDictionary: [String: String] = [
    "valueOne" : "bar",
    "valueTwo" : "foo"
]
let identifier = controllersDictionary[value]!
let viewController = storyboard.instantiateViewController(withIdentifier: identifier)

演员表在您显示的代码中对您没有任何作用。 viewController 类型为 UIViewController,但由于多态性,它是正确的底层视图控制器子类;无论故事板中的类是什么,这就是该实例的类。

唯一需要强制转换的情况是当您必须使用仅属于子类的消息向实例发送消息时,并且此时您的代码中没有显示任何此类需求。

【讨论】:

    【解决方案3】:

    虽然有/将会有办法让这种事情发挥作用,但 Swifty 解决方案 (IMO) 是让您想要的类遵守定义您尝试使用的共享行为的协议,或者简单地使用他们有共同的超类

    这允许所需的动态性(至少在大多数情况下),同时仍然允许防止运行时错误的编译时检查。

    以你的为例,

    protocol Stringable {
        func toString() -> String
    }
    
    extension String: Stringable {
        func toString() -> String {
            return self
        }
    }
    
    let thing = "foo"
    let anything: Any = thing
    let test: String? = (anything as? Stringable)?.toString()
    

    请注意,这需要“Any”而不是“AnyObject”,因为您需要强制转换为协议

    既然你提到了 ViewControllers,我认为这可能会有所帮助:

    static func createViewController<T: UIViewController>(storyboard: String, scene: String) -> T? {
        return  UIStoryboard(name: storyboard, bundle: nil).instantiateViewControllerWithIdentifier(scene) as? T
    }
    

    【讨论】:

    • 您提供的故事板示例非常接近我想要的!这会很好用。我只是想看看是否有办法按照我的意愿去做。但是谢谢你:)
    【解决方案4】:

    x as! Y.Type 语法仅在明确声明 Y 时才有效。

    所以,编译器需要x as! NSString。编译器崩溃是一个bug,建议你提交报告。

    想一想,这对你有什么帮助? stringClass 属于 AnyClass 类型,您正在强制 投射 AnyObject已经符合 AnyClass。考虑到静态类型时应该进行强制转换,因为否则强制转换没有任何意义。

    但是,如果您想检查对象的类是否是特定类型的 子类,您可以使用:

    x is Y.Type

    【讨论】:

    • "当你想到一个静态类型时,你应该强制转换,否则强制转换没有任何意义。"确切地。如果静态类型系统的其余部分不知道您的动态类型,那有什么好处呢?
    • 请查看我更新后的问题和真实示例,看看我想要实现的目标是否更清楚。
    • @AlexanderMomchliov 请不要再暗示我的问题是一个“坏”的问题,这并不真正符合社区精神。如果对您来说不够清楚,我很抱歉
    • @AlexCurran 我很抱歉,如果它以这种方式出现,但我根本不想这样。我只是建议您想到的解决方案可能不是用静态语言解决您遇到的问题的可行解决方案。如果您向我们提供有关全局的更多详细信息,我们可以提出更合适的解决方案
    • 没问题,谢谢回复。我现在用确切的用例更新了我的问题:)
    【解决方案5】:

    只要您可以向编译器提供关于...T 的类型的“提示”,这是可能的。所以在下面的例子中必须使用: String?

    func cast<T>(_ value: Any) -> T? {
        return value as? T
    }
    
    let inputValue: Any = "this is a test"
    let casted: String? = cast(inputValue) 
    print(casted) // Optional("this is a test")
    print(type(of: casted)) // Optional<String>
    

    为什么 Swift 不允许我们 let casted = cast&lt;String&gt;(inputValue) 我永远不会知道。


    一个恼人的场景是你的函数没有返回值。那么它并不总是直截了当地提供必要的“提示”。让我们看看这个例子......

    func asyncCast<T>(_ value: Any, completion: (T?) -> Void) {
        completion(value as? T)
    }
    

    以下客户端代码无法编译。它给出了“无法推断通用参数'T'”错误。

    let inputValue: Any = "this is a test"
    asyncCast(inputValue) { casted in
        print(casted) 
        print(type(of: casted))
    }
    

    但是您可以通过向编译器提供如下“提示”来解决这个问题:

    asyncCast(inputValue) { (casted: String?) in
        print(casted) // Optional("this is a test")
        print(type(of: casted)) // Optional<String>
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-05
      • 1970-01-01
      • 2012-05-16
      • 2013-11-07
      相关资源
      最近更新 更多