首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在运行时确定协议类型

在运行时确定协议类型
EN

Stack Overflow用户
提问于 2015-01-03 09:29:35
回答 3查看 220关注 0票数 1

如何根据用户提供的实例确定协议是否符合特定的子类型,如果不可能采用这种方式,则确定任何替代解决方案。

API

代码语言:javascript
复制
protocol Super {}

protocol Sub: Super {} //inherited by Super protocol

class Type1: Super {} //conforms to super protocol

class Type2: Type1, Sub {} //conforms to sub protocol

在另一个API类中

代码语言:javascript
复制
func store(closures: [() -> Super]) {
    self.closures = closures
}

是时候打电话了

代码语言:javascript
复制
func go() {
    for closure in closures {
        var instance = closure()
        if instance is Super {
            //do something - system will behave differently
        } else { //it's Sub
            //do something else - system will behave differently
        }
    }
}

api的用户

代码语言:javascript
复制
class Imp1: Type1 {}
class Imp2: Type2 {}

var closures: [() -> Super] = [ { Imp1() }, { Imp2() } ]
store(closures)

我当前在API中的解决方案

代码语言:javascript
复制
func go() {
        for closure in closures {
            var instance = closure()
            var behavior = 0
            if instance as? Type2 != nil { //not so cool, should be through protocols
                behavior = 1         //instead of implementations
            }


            if behavior == 0 { //do something within the api,

            } else { //do something else within the api

            }

            //instance overriden method will be called 
            //but not important here to show, polymorphism works in here
            //more concerned how the api can do something different based on the types

        }
    }
EN

回答 3

Stack Overflow用户

发布于 2015-01-03 14:04:54

您正在通过大量的循环手动重新创建动态调度,即协议和类的目的之一。实际使用真实的运行时多态性来解决您的问题。

取此代码:

代码语言:javascript
复制
    if instance is Super {
        //do something
    } else { //it's Sub
        //do something else
    }

您的意思是,如果是超类,运行超类方法,否则,运行子类。这有点颠倒--通常,当您是子类时,您希望运行子类代码,而不是相反。但是,假设您按照更常规的顺序将其转过来,那么您实际上是在描述调用协议的方法,并期望调用适当的实现:

(闭包实际上与当前的问题无关,所以暂时忽略它们)

代码语言:javascript
复制
protocol Super { func doThing() }
protocol Sub: Super { }  // super is actually a bit redundant here

class Type1: Super {
    func doThing() {
        println("I did a super thing!")
    }
}

class Type2: Sub {
    func doThing() {
        println("I did a sub thing!")
    }
}

func doSomething(s: Super) {
    s.doThing()
}

let c: [Super] = [Type1(), Type2()]

for t in c {
    doSomething(t)
}

// prints “I did a super thing!”, then “I did a sub thing!"

要考虑的其他选择:消除Sub,让Type2继承Type1。或者,由于这里没有类继承,所以可以使用structs而不是类。

票数 1
EN

Stack Overflow用户

发布于 2015-01-04 19:08:04

几乎任何时候,当您发现自己想使用is?时,您可能都会使用枚举。Enum允许您使用相当于is?的方法,而不必为此感到难过(因为它不是一个问题)。is?设计不好的原因是它创建了一个关闭子类型的函数,而OOP本身总是对子类型开放(您应该将final看作编译器优化,而不是类型的基本部分)。

不接受子类型不是问题,也不是坏事。它只需要以功能范式而不是对象范式来思考。Enum(它是求和类型的Swift实现)正是实现这个目的的工具,并且通常是一个比子类更好的工具。

代码语言:javascript
复制
enum Thing {
    case Type1(... some data object(s) ...)
    case Type2(... some data object(s) ...)
}

现在在go()中,不是is?检查,而是switch。这不仅不是一件坏事,它是必需的,并由编译器进行全面的类型检查。

(示例删除了延迟闭包,因为它们实际上不是问题的一部分。)

代码语言:javascript
复制
func go(instances: [Thing]) {
    for instance in instances {
        switch instance {
            case Type1(let ...) { ...Type1 behaviors... }
            case Type2(let ...) { ...Type2 behaviors... }
        }
    }
}

如果您有一些共享行为,只需将这些行为提取到函数中即可。您可以自由地让您的“数据对象”实现某些协议,或者具有特定的类,如果这样可以更容易地传递给共享函数。如果Type2接收的关联数据恰好是Type1的一个子类,那就好了。

如果您稍后加入并添加一个Type3,那么编译器将警告您注意没有考虑到这一点的每个switch。这就是为什么枚举是安全的,而is?则不安全。

票数 1
EN

Stack Overflow用户

发布于 2015-01-03 09:46:04

您需要从objects世界派生的对象才能做到这一点:

代码语言:javascript
复制
@objc protocol Super {}

@objc protocol Sub: Super {}

class Parent: NSObject, Super {}

class Child: NSObject, Sub {}

func go( closures: [() -> Super]) {
  for closure in closures {
    let instance = closure()
    if instance is Sub { // check for Sub first, check for Super is always true
      //do something
    } else {
      //do something else
    }
  }
}

编辑:具有不同方法实现的版本:

代码语言:javascript
复制
protocol Super {
  func doSomething()
}

protocol Sub: Super {}

class Parent: Super {
  func doSomething() {
    // do something
  }
}

class Child: Sub {
  func doSomething() {
    // do something else
  }
}

func go( closures: [() -> Super]) {
  for closure in closures {
    let instance = closure()
    instance.doSomething()
  }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/27753405

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档