Swift 中有没有办法部分匹配泛型?

Is there a way in Swift to partially match a generic?

也就是说,如果我有一个采用两个泛型 A 和 B 的 class C,有没有一种方法可以将对象转换为 C 而我不关心 B 是什么?

我的具体用例是我需要在多 window 但非基于文档的应用程序中连接 NSView 功能和新的 SwiftUI。我遇到的问题是,给定一个 NSView,我需要获取它正在管理的 SwiftUI 视图(在我的例子中是一个名为 ContentView 的视图)。

请注意,我确实有一个解决方案,包括在下面,但它涉及使用基于 Mirror 的反射,我想知道是否有更好的方法,很可能涉及使用 as? 转换为泛型的部分匹配。

桥接是使用 NSHostingView 完成的,因此看起来应该只执行以下操作:

if let hostingView = NSApplication.shared.keyWindow?.contentView as? NSHostingView<ContentView> {
    // do what I need with 'hostingView.rootView'
}

不幸的是,NSHostingView.rootView 不是 return 我创建的实际 ContentView,它 return 是该视图的修改版本,具体取决于所使用的修饰符。 (在我的例子中,我使用的是 .environmentObject 修饰符。)因此,上面的 if 语句永远不会 return 为真,因为类型不是 NSHostingView<ContentView> 而是 NSHostingView<ModifiedContent<ContentView, _bunch_Of_Gobbletygook_Representing_The_Modifiers>>. "solve" 问题的一种方法是在我创建 window 时打印出 type(of: hostingView) 的结果,然后更改我的转换以包含 "gobbledygook" 的当前版本,但由于以下两个原因,它很脆弱:

  1. 如果我更改修饰符,编译器不会警告我需要更新转换,并且
  2. 由于 "gobbledygook" 包含单个下划线值,我必须假设这些是可能更改的内部细节。因此,如果我不更改任何代码,OS 更新可能会导致转换开始失败。

所以我创建了以下 NSView 扩展形式的解决方案:

extension NSView {
    func originalRootView<RootView: View>() -> RootView? {
        if let hostingView = self as? NSHostingView<RootView> {
            return hostingView.rootView
        }
        let mirror = Mirror(reflecting: self)
        if let rootView = mirror.descendant("_rootView") {
            let mirror2 = Mirror(reflecting: rootView)
            if let content = mirror2.descendant("content") as? RootView {
                return content
            }
        }
        return nil
    }
}

这让我可以使用以下方式处理我的需求:

private func currentContentView() -> ContentView? {
    return NSApplication.shared.keyWindow?.contentView?.originalRootView()
}

... sometime later ...

if let contentView = currentContentView() {
    // do what I need with contentView
}

我想知道是否有一种方法可以在不使用反射的情况下实现 originalRootView,大概是通过允许对 ModifiedContent 对象进行部分指定的强制转换。例如,类似于以下内容(无法编译):

extension NSView {
    func originalRootView<RootView: View>() -> RootView? {
        if let hostingView = self as? NSHostingView<RootView> {
            return hostingView.rootView
        }
        if let hostingView = self as? NSHostingView<ModifiedContent<RootView, ANY>> {
            return hostingView.rootView.content
        }
        return nil
    }
}

问题是要为 "ANY" 添加什么。我会想到某种形式的 AnyAnyObject,但编译器对此有所抱怨。本质上,我想告诉编译器我不关心 ANY 是什么,只要 ModifiedContent 的内容类型是 RootView。

如有任何想法,我们将不胜感激。

只是为了让结果正式,答案是“不”,从 Swift 5.1 开始,无法部分匹配通用。