Swift 协议、结构,但没有具体类型

Swift 协议、结构,但没有具体类型,swift,generics,protocols,Swift,Generics,Protocols,好的,我有一个名为Environment protocol Environment { var rootURL: String {get} } 然后是两个结构: struct Production: Environment { var rootURL = "www.api.mybackend.com/v1" } struct Development: Environment { var rootURL = "www.api.mydevelopmentbackend.com/v1"

好的,我有一个名为
Environment

protocol Environment {
  var rootURL: String {get}
}
然后是两个结构:

struct Production: Environment {
  var rootURL = "www.api.mybackend.com/v1"
}

struct Development: Environment {
  var rootURL = "www.api.mydevelopmentbackend.com/v1"
}
具有检索环境的函数的设置对象:

class Settings {
  func getEnvironment<T>() -> T where T: Environment {
    let environmentRaw = self.retreiveEnvironmentFromLocalStore()
    switch environmentRaw {
    case 0:
        return Development() as! T
    case 1:
        return Production() as! T
    default:
        return Development() as! T
    }
  }

  func retreiveEnvironmentFromLocalStore() -> Int {
    //Realm, SQLLite, Core Date, I don't really care for this example. 
    //Let's just say it defaults to returning 1
  }
}
类设置{
func getEnvironment()->T其中T:Environment{
让environmentRaw=self.retreiveEnvironmentFromLocalStore()
交换机环境RAW{
案例0:
将Development()返回为!T
案例1:
将生产()返回为!T
违约:
将Development()返回为!T
}
}
func RetreiveEnvironment fromLocalStore()->Int{
//Realm、SQLLite、Core Date,我并不真正关心这个示例。
//假设它默认返回1
}
}
我真的很想继续前进,但现在当我在设置对象上调用此函数并尝试使用
rootURL
属性时,编译器抱怨它无法识别类型。因此,为了更好地理解并找到解决方案:

1) 如果我正在访问由协议定义的属性,而它至少知道返回的类型符合该协议,那么为什么它会关心该类型

2) 结构没有继承性。我应该定义一个基类而忘记泛型吗

3) 我可以使我的
getEnvironment
功能更好吗?我不喜欢强制铸造,它看起来像一种代码气味

4) 我在这里正确使用泛型了吗

编辑1:

要明确的是,我想要一个函数,它返回一个我知道将具有此属性的结构

我在这里正确使用泛型了吗

不,我不这么认为。您的意思是
getEnvironment
将返回
T
,它可以是客户机代码指定的任何类型,只要它实现
Environment
。然而,该方法的实现并没有做到这一点。它只返回两种
环境
s,返回哪种类型不取决于客户端代码。返回的类型取决于
retreiveenvironmentfromloctore
返回的内容。因此,泛型不适用于这种情况

因为它返回的环境类型是由方法的实现决定的,而不是由客户机代码决定的,所以您应该在这里使用多态性-让它返回一个
环境

  func getEnvironment() -> Environment {
    let environmentRaw = self.retreiveEnvironmentFromLocalStore()
    switch environmentRaw {
    case 0:
        return Development()
    case 1:
        return Production()
    default:
        return Development()
    }
  }
我在这里正确使用泛型了吗

不,我不这么认为。您的意思是
getEnvironment
将返回
T
,它可以是客户机代码指定的任何类型,只要它实现
Environment
。然而,该方法的实现并没有做到这一点。它只返回两种
环境
s,返回哪种类型不取决于客户端代码。返回的类型取决于
retreiveenvironmentfromloctore
返回的内容。因此,泛型不适用于这种情况

因为它返回的环境类型是由方法的实现决定的,而不是由客户机代码决定的,所以您应该在这里使用多态性-让它返回一个
环境

  func getEnvironment() -> Environment {
    let environmentRaw = self.retreiveEnvironmentFromLocalStore()
    switch environmentRaw {
    case 0:
        return Development()
    case 1:
        return Production()
    default:
        return Development()
    }
  }
我真的希望继续朝着面向协议的编程方向前进

相反,我建议您尝试朝着一个清晰、可理解的代码方向前进,而不管流行方向是什么

以下是您的函数声明:

func getEnvironment()->T其中T:Environment

这意味着
getEnvironment()
将返回某种类型的对象
T
,其中
T
在编译时根据调用
getEnvironment()
的代码推导而来

T
可以是什么类型?它可以是
生产
开发
。例如,您可以编写:

let e: Production = Settings().getEnvironment()
这使编译器可以推断
getEnvironment()
在此调用站点返回一个
Production
(这是一个
环境

但是有一个问题:
getEnvironment()
可能会根据
retreiveEnvironment fromLocalStore
中的随机数生成器,尝试返回一个
Development
。然后,当它无法将
Development
转换为
Production
时,您将在运行时得到崩溃

为什么您认为
getEnvironment()
需要通用?根据你问题中的代码,它不应该是

import Foundation

protocol Environment {
    var rootURL: String {get}
}

struct Production: Environment {
    var rootURL = "www.api.mybackend.com/v1"
}

struct Development: Environment {
    var rootURL = "www.api.mydevelopmentbackend.com/v1"
}

class Settings {
    func getEnvironment() -> Environment {
        let environmentRaw = self.retreiveEnvironmentFromLocalStore()
        switch environmentRaw {
        case 1: return Production()
        default: return Development()
        }
    }

    func retreiveEnvironmentFromLocalStore() -> Int {
        return Int(arc4random())
    }
}


let settings = Settings()
let e = settings.getEnvironment()

款式说明:建议我们

  • 根据其副作用命名函数和方法

    • 那些没有副作用的应该读作名词短语,例如
      x.distance(to:y),i.succession()
除非
Settings
中的方法有重要的副作用,否则更好的名称应该是
environment()
rawEnvironmentFromLocalStore()

我真的希望继续朝着面向协议的编程方向前进

相反,我建议您尝试朝着一个清晰、可理解的代码方向前进,而不管流行方向是什么

以下是您的函数声明:

func getEnvironment()->T其中T:Environment

这意味着
getEnvironment()
将返回某种类型的对象
T
,其中
T
在编译时根据调用
getEnvironment()
的代码推导而来

T
可以是什么类型?它可以是
生产
开发
。例如,您可以编写:

let e: Production = Settings().getEnvironment()
这使编译器可以推断
getEnvironment()
在此调用站点返回一个
Production
(这是一个
环境

但是有一个问题:
getEnvironment()
可能会根据
retreiveEnvironment fromLocalStore
中的随机数生成器,尝试返回一个
Development
。然后,在运行时,当它无法强制转换时,您将得到一个崩溃