【问题标题】:Creating a new instance of a class with a protocol-conforming class as an argument使用符合协议的类作为参数创建类的新实例
【发布时间】:2021-05-09 23:08:36
【问题描述】:

我有以下代码设置(用 Swift 编写):

protocol DataFetcherType {
    init(_ mainData: String, fetchData: Bool)
}

class DataFetcher1: DataFetcherType {
    required init(_ mainData: String, fetchData: Bool) {
    }
}

class DataFetcher2: DataFetcherType {
    required init(_ mainData: String, fetchData: Bool) {
    }
}

struct Data<FetcherType: DataFetcherType> {
    var mainData: String
    var fetcher: DataFetcherType
    
    init(_ mainData: String, using fetcher: FetcherType, fetchData: Bool = true) {
        self.mainData = mainData
        self.fetcher = FetcherType(mainData, fetchData: fetchData)
    }
}

我正在尝试像这样实例化Data

Data("foo", using: DataFetcher1)

但我得到了错误:

类型“DataFetcher1.Type”不能符合“DataFetcherType”

如果我尝试使用DataFetcher2,也是如此。

我已经做了好几个小时了,我觉得我已经改变了所有可能的事情,但我就是无法让它发挥作用。我最初的尝试没有使用泛型,但我最终决定这可能是我需要做的。这是错的,还是我从根本上错过了 Swift 工作原理的一些东西?

【问题讨论】:

  • 在您的数据结构初始化程序中执行 self.fetcher = fetcher,这正是您想要实现的目标?
  • @Kazi 不,因为我仍然需要使用 mainData 和其他参数初始化 fetcher。目标是实例化Data 并让它自动/异步获取相关数据

标签: swift generics instantiation swift-protocols


【解决方案1】:

您根本不需要传递任何类型。 using 参数完全没有必要。删除它。这是通用的;传递类型使泛型毫无意义!

当您声明要实例化的内容时,只需解析泛型即可。 (并且不要称某些东西为 Data,该名称已被使用。)

所以,如果我们从这个开始:

protocol DataFetcherType {
    init(_ mainData: String, fetchData: Bool)
}
class DataFetcher1: DataFetcherType {
    required init(_ mainData: String, fetchData: Bool) {
    }
}
class DataFetcher2: DataFetcherType {
    required init(_ mainData: String, fetchData: Bool) {
    }
}

...那么这就是你所需要的:

struct MyData<FetcherType: DataFetcherType> {
    var mainData: String
    var fetcher: FetcherType
    init(_ mainData: String, fetchData: Bool = true) {
        self.mainData = mainData
        self.fetcher = FetcherType.init(mainData, fetchData: fetchData)
    }
}

...因为要制作一个,你只需解析类型即可:

    let mydata = MyData<DataFetcher1>("hey")

另外,请注意我更改了变量fetcher 的类型。它应该是泛型类型,而不是协议

一般来说,如果你发现自己在 Swift 中传递类型,那是一种不好的气味,你应该重新考虑。可以做到,有时也可以做到,但总的来说 Swift 不喜欢元类型,你应该避免它们。

协议也是如此;如果您发现您正在输入一些作为协议的内容,请重新考虑。

这正是泛型存在的原因,所以你不要做这些事情。

【讨论】:

  • 嘿,非常感谢。这很有道理,你完美地回答了我的问题。但我意识到我没有传达我想要的细节。在创建MyData 实例时,我想远离通用语法,如果可能的话,理想情况下,将类似枚举的东西作为参数传递,这样调用就会是let mydata = MyData("hey", using: .DataFetcher1)。我认识到您的解决方案实现了相同的目标,但我试图使调用语法尽可能优雅/可读,即使它可能被认为是一种不好的做法。想法?
  • 信息不足。你为什么想这么做?每当您发现自己打开了这样的类型时,这听起来就像是糟糕的 OOP。
【解决方案2】:

您必须通过参数获取类型而不是对象。

将你的数据结构初始化器重写为:

struct Data<FetcherType: DataFetcherType> {
    var mainData: String
    var fetcher: DataFetcherType
    
    init(_ mainData: String, using fetcher: FetcherType.Type, fetchData: Bool = true) {
        self.mainData = mainData
        self.fetcher = FetcherType(mainData, fetchData: fetchData)
    }
}

let data = Data("Data fetcher1", using: DataFetcher2.self)
print(data.fetcher)

【讨论】:

  • 很抱歉,这是不正确的。您不需要传递类型,这样做会使泛型完全没有意义。
  • 而且将变量键入为协议也是错误的。
  • @matt 你是完全正确的,但我试图只解决他的错误,是的,根据他的实现,当他可以通过初始化程序获取类型时,没有理由使用泛型。但我不明白为什么键入一个变量作为协议是错误的?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-25
  • 2016-09-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多