Arrays 如何在Swift中使函数的返回类型通用 简介
在我的应用程序中,我有一个名为“ElementData”的超类和几个继承自它的子类 每个子类都有自己的validateModel()方法,该方法返回 不同的类型,取决于类-始终在数组中 换句话说:该方法在每个子类中返回不同的类型 例子 A类:Arrays 如何在Swift中使函数的返回类型通用 简介,arrays,swift,class,generic-programming,Arrays,Swift,Class,Generic Programming,在我的应用程序中,我有一个名为“ElementData”的超类和几个继承自它的子类 每个子类都有自己的validateModel()方法,该方法返回 不同的类型,取决于类-始终在数组中 换句话说:该方法在每个子类中返回不同的类型 例子 A类:func validateModel()->[String] B类:func validateModel()->[Int] C类:func validateModel()->[MyCustomEnum] 如您所见,只有返回值彼此不同 EDIT:validat
func validateModel()->[String]
B类:func validateModel()->[Int]
C类:func validateModel()->[MyCustomEnum]
如您所见,只有返回值彼此不同
EDIT:validateModel()方法的示例:
func validateModel() -> [DefaultElementFields]{ // DefaultElementFields is an enum with the different view types of my collection view
var checkResult: [DefaultElementFields] = []
if name == "" {
checkResult.append(.Name)
}
if Int(rewardedPoints) == nil {
checkResult.append(.Points)
}
if description == "" {
checkResult.append(.Description)
}
if selectedImage == nil {
checkResult.append(.Image)
}
return checkResult
}
func validateModel() -> [Int] { // returns the index of the text field which is wrong
var checkResult: [Int] = []
let filledValues = codes.filter {
$0 != ""
}
if filledValues.count == 0 { // if no values have been entered, all fields should be marked red.
checkResult.append(-1)
return checkResult
}
for (i, code) in codes.enumerated() {
if code != "" && (code.count < 3 || code.count > 10 || code.rangeOfCharacter(from: NSCharacterSet.alphanumerics.inverted) != nil){ // code must have more than 3 and less than 11 characters. No symbols are allowed.
checkResult.append(i)
}
}
return checkResult
}
class ElementData {
func validateModel<T>() -> [T] {
return [1] as! [T] // just a test return
}
}
dataObject.validateModel() // dataObject inherits from ElementData -> has access to validateModel()
A类:
func validateModel() -> [DefaultElementFields]{ // DefaultElementFields is an enum with the different view types of my collection view
var checkResult: [DefaultElementFields] = []
if name == "" {
checkResult.append(.Name)
}
if Int(rewardedPoints) == nil {
checkResult.append(.Points)
}
if description == "" {
checkResult.append(.Description)
}
if selectedImage == nil {
checkResult.append(.Image)
}
return checkResult
}
func validateModel() -> [Int] { // returns the index of the text field which is wrong
var checkResult: [Int] = []
let filledValues = codes.filter {
$0 != ""
}
if filledValues.count == 0 { // if no values have been entered, all fields should be marked red.
checkResult.append(-1)
return checkResult
}
for (i, code) in codes.enumerated() {
if code != "" && (code.count < 3 || code.count > 10 || code.rangeOfCharacter(from: NSCharacterSet.alphanumerics.inverted) != nil){ // code must have more than 3 and less than 11 characters. No symbols are allowed.
checkResult.append(i)
}
}
return checkResult
}
class ElementData {
func validateModel<T>() -> [T] {
return [1] as! [T] // just a test return
}
}
dataObject.validateModel() // dataObject inherits from ElementData -> has access to validateModel()
B类:
func validateModel() -> [DefaultElementFields]{ // DefaultElementFields is an enum with the different view types of my collection view
var checkResult: [DefaultElementFields] = []
if name == "" {
checkResult.append(.Name)
}
if Int(rewardedPoints) == nil {
checkResult.append(.Points)
}
if description == "" {
checkResult.append(.Description)
}
if selectedImage == nil {
checkResult.append(.Image)
}
return checkResult
}
func validateModel() -> [Int] { // returns the index of the text field which is wrong
var checkResult: [Int] = []
let filledValues = codes.filter {
$0 != ""
}
if filledValues.count == 0 { // if no values have been entered, all fields should be marked red.
checkResult.append(-1)
return checkResult
}
for (i, code) in codes.enumerated() {
if code != "" && (code.count < 3 || code.count > 10 || code.rangeOfCharacter(from: NSCharacterSet.alphanumerics.inverted) != nil){ // code must have more than 3 and less than 11 characters. No symbols are allowed.
checkResult.append(i)
}
}
return checkResult
}
class ElementData {
func validateModel<T>() -> [T] {
return [1] as! [T] // just a test return
}
}
dataObject.validateModel() // dataObject inherits from ElementData -> has access to validateModel()
不幸的是,它不起作用,我得到以下错误:
“无法推断泛型参数'T'”
总结:
- 我有一个超类“ElementData”和几个子类(继承的类)
- 每个子类都有一个验证模型的方法validateModel()
- 只有子类中validateModel()方法的返回类型不同——因此我想将该方法放在父类(ElementData)中,然后在子类上覆盖它
func identity(_ value: Any) -> Any {
return value
}
它实际上不起作用:
let i = 5
assert(identity(i) == i) // ❌ binary operator '==' cannot be applied to operands of type 'Any' and 'Int'
任何
都会导致类型信息丢失。尽管我们看到参数的类型和返回值总是相同的,但我们还没有向类型系统表达这一点。这是泛型类型参数的完美用例。它允许我们表达参数类型和返回值之间的关系
func identity<T>(_ value: T) -> T {
return value
}
let i = 5
assert(identity(i) == i) // ✅
func标识(u值:T)->T{
返回值
}
设i=5
断言(标识(i)=i)//✅
什么是非专利药
回顾您的问题,您会发现这里没有要表达的类型关系
始终返回ClassA.validateModel()
[String]
始终返回ClassB.validateModel()
[Int]
始终返回ClassC.validateModel()
[MyCustomEnum]
ElementData
的对象。该对象可以是ElementData
,或ClassA
,或ClassB
,或ClassC
的实例。假设这四种类型都是可能的,并且假设存在一些混合物来做您想要做的事情,那么这段代码将如何工作
let elementData = someElementData()
let validatedModel = elementData.validateModel() // A possible solution is a protocol with associated types. You have to specify the return type as typealias
in each subclass.
protocol Validatable {
associatedtype ReturnType
func validateModel() -> [ReturnType]
}
class ElementData {}
class SubClassA : ElementData, Validatable {
typealias ReturnType = Int
func validateModel() -> [Int] { return [12] }
}
class SubClassB : ElementData, Validatable {
typealias ReturnType = String
func validateModel() -> [String] { return ["Foo"] }
}
let elementData=someElementData()
让validatedModel=elementData.validateModel()//一个可能的解决方案是使用关联类型的协议。必须在每个子类中将返回类型指定为typealias
现在编译器知道所有子类的不同返回类型
请向我们展示至少两种验证方法的实现。有两种可能性。使用协议而不是超类,或者使整个类成为泛型。要推断泛型类型,必须执行类似于let array:[Int]=dataObject.validateModel()的操作
使用返回值推断类型。@Sulthan但为了访问该方法并知道它返回什么,我仍然必须向下转换每个对象,对吗?顺便说一句:请参阅edit到目前为止,我在您的方法中没有看到任何共同点,我不知道您为什么要平等地调用这些方法。@Sulthan方法中的代码在每个类中都是不同的-但是基本函数只在返回类型上有所不同。现在我必须打开单元格的类型才能知道它是哪个elementDataClass。如果我想在顶级类中使用validateModel方法,我可以在每个DataElementClass上调用它,而无需强制转换它。我希望现在更清楚:)非常感谢你的回答。为了避免打开我的单元格类型,然后丢弃数据对象,您有没有其他方法可以遵循?我有点迷路了,因为我不知道如何改进我的课程/模型…@linus_全息图我正在研究,但我花了一段时间来解释effectively@linus_hologram现在查看谢谢您的详细解释。那么,populate方法为单元格返回一个完成的uiview?或者我误解了什么?在我的示例中,TableViewDelegate将单元格出列并传递给模型,以便模型可以填充其数据(通过修改单元格对象,该对象通过引用传递)。在这种情况下返回对象实际上不会实现任何效果。但具体细节并不太重要。您只需要委派填充目标视图(表单元格或其他任何内容)的责任。谢谢您的回答。但是有没有办法将validateModel()方法移动到父类中?子类只覆盖它?因为我遇到的实际问题是,为了访问validateModel(),我希望避免向下转换类?不,不是异构返回类型。别管它!我还没有尝试过,但我认为您的解决方案可以工作。如果我试图将我的类降级为Validatable,则会出现以下错误:“协议‘Validatable’只能用作泛型约束,因为它具有自身或关联的类型要求”。在尝试使用该函数时,我遇到了另一个错误:“成员'validateModel'不能用于协议类型'Validatable'的值;请改用通用约束”如何访问在可验证协议中指定的函数?我不知道您将要完成什么。我的答案是一种我想要的方式