【问题标题】:How to store generic type to avoid internal struct requiring it in Swift?如何存储泛型类型以避免在 Swift 中需要它的内部结构?
【发布时间】:2020-09-06 12:51:39
【问题描述】:

这个问题最好用一些代码来解释。

步骤 1

public struct Example<Content: View> {
    let content: () -> Content
    
    init(@ViewBuilder content: @escaping () -> Content) {
        self.content = content
    }
    
    func contentView() -> Content {
        self.content()
    }
}

第二步

现在当我添加一个内部结构时,编译器会抱怨不支持静态存储属性。

public struct Example<Content: View> {
    let content: () -> Content
    
    init(@ViewBuilder content: @escaping () -> Content) {
        self.content = content
    }
    
    func contentView() -> Content {
        self.content()
    }
    
    public struct ActionKey: Hashable, Equatable, RawRepresentable {
        public static let cancelButtonClicked = ActionKey("cancelButtonClicked") // Static stored properties not supported in generic types

        public static func == (lhs: ActionKey, rhs: ActionKey) -> Bool {
            return lhs.rawValue == rhs.rawValue
        }
        
        public let rawValue: String
        
        public init(rawValue: String) {
            self.rawValue = rawValue
        }
        
        public init(_ rawValue: String) {
            self.init(rawValue: rawValue)
        }
    }
}

第三步

为了消除错误,我们需要将其转换为计算变量。

public static var cancelButtonClicked: ActionKey { get { ActionKey("cancelButtonClicked") } }

问题

除了这已经很烦人之外,它变得更糟。我们还需要为一个完全不依赖它的结构提供一个通用参数。

_ = Example.ActionKey(rawValue: "cancelButtonClicked") // Generic parameter 'Content' could not be inferred

// Fix
_ = Example<AnyView>.ActionKey(rawValue: "cancelButtonClicked") 

如果我们能以某种方式避免将泛型类型放在外部作用域中,我们就可以避免它。但是,将类型存储在变量中不会让我使用它。所以我被困住了。有人回答吗?

public struct Example<Content: View> {
    let content: Any
    let contentType: Any.Type
    
    init<Content>(@ViewBuilder content: @escaping () -> Content) {
        self.content = content
        self.contentType = Content.self
    }
    
    func contentView() -> ?? {
        self.content() // ??
    }
    
    public struct ActionKey: Hashable, Equatable, RawRepresentable {
        public static var cancelButtonClicked: ActionKey { get { ActionKey("cancelButtonClicked") } }

        public static func == (lhs: ActionKey, rhs: ActionKey) -> Bool {
            return lhs.rawValue == rhs.rawValue
        }
        
        public let rawValue: String
        
        public init(rawValue: String) {
            self.rawValue = rawValue
        }
        
        public init(_ rawValue: String) {
            self.init(rawValue: rawValue)
        }
    }
}

将内部结构放在外面是唯一的解决方案吗?

【问题讨论】:

  • 获取非泛型ActionKey,实际上是独立的,类型出泛型Example类型。
  • 当然可以把它放在外面。但是内部结构会更有意义,因为 ActionKey 仅属于 Example。因此,Example.ActionKey 和单独在结构中使用 ActionKey 比将结构放在外面并调用它 ExampleActionKey 更好。
  • 但这是您未能掌握的部分。你永远不能说 Example.ActionKey 因为没有这样的类型。示例不是单一类型。它是通用的!这意味着Example&lt;String&gt; 是一个类型,Example&lt;Int&gt; 是一个独立且不相关的类型。您正在尝试为其中的每一个定义一个内部类型。

标签: swift generics swiftui


【解决方案1】:

这是一种可能的方法(但我会把它放在外面,比如ButtonStyleButton 之外)......无论如何,它是:

public struct Example {
    private let content: AnyView

    init<Content: View>(@ViewBuilder content: @escaping () -> Content) {
        self.content = AnyView(content())
    }

    func contentView() -> some View {
        self.content
    }

    public struct ActionKey: Hashable, Equatable, RawRepresentable {
        public static let cancelButtonClicked = ActionKey("cancelButtonClicked") // Static stored properties not supported in generic types

        public static func == (lhs: ActionKey, rhs: ActionKey) -> Bool {
            return lhs.rawValue == rhs.rawValue
        }

        public let rawValue: String

        public init(rawValue: String) {
            self.rawValue = rawValue
        }

        public init(_ rawValue: String) {
            self.init(rawValue: rawValue)
        }
    }
}

【讨论】:

  • 感谢 Asperi!把它放在外面也是很好的提示。在内部,我可以使用 typealias 来避免输入所有这些长名称 ;-)
猜你喜欢
  • 1970-01-01
  • 2015-06-08
  • 2017-04-14
  • 1970-01-01
  • 2022-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多