理解“在通用上下文中查找动态类型”

问题描述 投票:0回答:2

我想了解这一部分。

我正在阅读在通用上下文中查找动态类型,其中包含以下代码片段:

func printGenericInfo<T>(_ value: T) {
    let t = type(of: value)
    print("'\(value)' of type '\(t)'")
}

protocol P {}
extension String: P {}

let stringAsP: P = "Hello!"
printGenericInfo(stringAsP)
// 'Hello!' of type 'P'

...接下来是这句话:

出现这种意外结果是因为

type(of: value)
内部对
printGenericInfo(_:)
的调用必须返回一个元类型,该元类型是
T.Type
的实例,
String.self
(预期的动态类型)不是
P.Type
的实例(值的具体元类型)。

  1. 当我可以运行此代码时,为什么
    String.self
    不是
    P.Type
    的实例?
func f(_ t: P.Type) { print("...") }

f(String.self)
  1. 为什么
    type(of:)
    在泛型函数外部而不是内部返回具体元类型?
print("'\(stringAsP)' of type '\(type(of: stringAsP))'")
// 'Hello!' of type 'String'
swift generics types protocols type-systems
2个回答
2
投票

对于协议

P
,您可以使用
P
编写两种元类型。您可以写
(any P).Type
(又名
P.Protocol
),这是
P
本身的元类型,以及
any P.Type
(又名
P.Type
),这是一种存在类型,表示“符合
 的某种类型的元类型” P
”。

String.self
any P.Type
的实例,但不是
(any P).Type
。这里的文档不正确。大概是想说
(any P).Type
。否则这整件事就没有意义了。

type(of:)
有两种不同的行为,具体取决于其类型参数
T
的类型。你的
printGenericInfo
还有一个类型参数
T
,所以为了避免混淆,我将它们分别称为
typeof.T
printGenericInfo.T

如果

typeof.T
是不存在的类型,则返回
typeof.T
的元类型。如果
typeof.T
是存在(即协议)类型
any E
,它将返回
any E.Type
,而不是(any E).Type
。毕竟,前者更有用 - 它可以告诉您存在类型“包装”的实际类型。 
type(of:)
在这种情况下需要“解开”存在主义并“看看里面”。

let s1 = "" let s2: Any = s1 // typeof.T is String type(of: s1) // typeof.T is Any, an existential type, so type(of:) unwraps it and returns String.self // this would be pretty useless if it just returned Any.self type(of: s2)

这种行为上的差异正是你printGenericInfo

所缺乏的事实上,这种语义无法用Swift的语法来编写。这就是为什么 type(of:)
 
具有奇怪的签名 - 它返回一个 Metatype
 类型参数,似乎与它所接受的类型无关。

type(of:)

 使用特殊注释来允许编译器以特殊方式对 
type(of:)
 调用进行类型检查。根据 
typeof.T
Metatype
 可以是 
typeof.T.Type
(当 
typeof.T
 不存在时)或 
any typeof.T.Type
(当 
typeof.T
 存在时)。请注意,这是一个“编译时”检查。泛型函数的类型参数是在编译时决定的,而不是在运行时决定的。
printGenericInfo

不会打开

printGenericInfo.T

 并查看其内部。它只是直接将其传递给
type(of:)
,因此
typeof.T
被决定为
printGenericInfo.T
,一个类型参数,它不是一个存在类型。
当你调用 
printGenericInfo(stringAsP)

时,

printGenericInfo.T

 被决定为 
any P
 - 存在类型。这并没有改变
typeof.T
,它仍然是
printGenericInfo.T
,一种不存在的类型。所以在运行时,
type(of:)
返回
any P
的元类型,即
(any P).Type
如果您改为执行 
let t = type(of: value as Any)

,那么

type(of:)

 将会看到 
typeof.T
 是存在类型 (
Any
),因此它将解开存在类型并查看其内部。

原因是您使用

0
投票
实例化了

let = "this is a string"

您不会(也不能)实例化 
P

P

是一个协议,其他类型只能
符合
它。但最终,当为变量创建值时,您必须显式实例化类型。该类型可能符合协议,也可能不符合。一致性不仅仅是身份。
编辑,对您的问题进行更多澄清:

    String.self
  1. 不是

    P

     的实例。
    这是因为在编写 
    extension String: P {}
     时,你告诉它 
    String
     的任何 
    实例 都可以被视为 P
    。这意味着类型本身 
    String.self
     不符合 
    P
    。 (回到正题:一致性不是同一性。)

    在这一行
  2. let stringAsP: P = "Hello!"
  3. 中,您告诉编译器明确忘记该值是

    String

    。然后,编译器会生成此方法 
    printGenericInfo
    ,并将 
    P
     作为类型。然后你可以想到 
    type(of:)
     然后说“好吧,在方法定义中你说你传入了 
    P
    ,所以我会向你重复一遍。不需要费力地手动查找内容.”。如果您按照 Apple 的示例,首先将其强制转换为 
    Any
    ,则 
    type(of:)
     实际上会执行一些工作并查找显式类型。
    
        

© www.soinside.com 2019 - 2024. All rights reserved.