将协议类型P
的值传递给SWIFT中的方法时,您当前可以使用4种可能的拼写:
func f<T: P>(_ value: T)
(通用)
func f(_ value: some P)
("不透明参数")
func f(_ value: P)
("‘赤裸裸的’存在主义‘")
func f(_ value: any P)
("‘显式’存在主义")
在这些拼写中,(1)和(2)是同义词,目前(3)和(4)是同义词.在使用(1)和(2)之间没有差别,在(3)和(4)之间目前*没有差别;但在(1)/(2)和(3)/(4)之间,有is个差别.
f<T: P>(_: T)
是获取保证符合协议P
的concrete类型T
的参数的传统方式.这种获取参数的方式:
- 允许您在编译时和运行时访问具体类型
T
,因此您可以对T
本身执行操作
- 没有开销,因为类型
T
在编译时是已知的,并且编译器知道值的大小和布局,并且可以适当地设置堆栈/寄存器;它可以将给它的任何参数直接传递给方法
- 只能在参数类型为statically已知(在编译时)时调用;因此,可以使用具有
Self
或associatedtype
要求的协议类型来调用
- 在SE-0341 (Opaque Parameter Declarations)中引入的采用
some Protocol
的方法的版本是exactly,相当于用尖括号拼写的泛型版本.我将避免重复提议的内容,但在简介部分中,我详细说明了将此语法作为简化泛型参数拼写复杂性的一种方法
f(_: P)
is the traditional way of taking a parameter of an existential type which is guaranteed to conform to protocol P
. This way of taking a parameter:
- 不允许在编译时访问参数的具体基础类型;尽管这可以在运行时通过
type(of:)
动态访问
- Has runtime overhead both to pass an argument to the method, and when accessing the value inside of the method: because the type of the argument to the method might not be known statically (而当 the compiler still needs to know how to set up the stack and registers in order to call the method), the parameter must be boxed up in an "existential box", which has the interface of
P
and can dynamically pass methods along to the underlying concrete value. This both has a cost of allocating an additional "box" at runtime to hold the actual value inside of a consistently-sized and -laid-out container, as well as the cost of indirecting as method calls on the box must dynamically dispatch to the underlying type
- 无论参数的类型是否静态已知,都可以调用;因此,可以使用具有
Self
或associatedtype
要求的协议类型来调用cannot
- 协议类型之前的
any
关键字是在SE-0335 (Existential Any)中引入的,它有助于指示该类型被用作存在词.exactly等同于使用协议的裸名称right now(即any P == P
),但是已经有一些关于最终使用协议的裸名称来代替地表示some P
的讨论
因此,为了解决你的具体例子:
init(_ value: some BinaryInteger, radix: Int = 10, uppercase: Bool = false)
完全等同于原来的
init<T>(_ value: T, radix: Int = 10, uppercase: Bool = false)
where T : BinaryInteger
而当
init(_ value: any BinaryInteger, radix: Int = 10, uppercase: Bool = false)
是not.而且,因为BinaryInteger
有associatedtype
个需求,所以您也可以使用any
版本,因为存在类型不会提供对基础关联类型的访问.(如果你试一试,你会得到classic 的error: protocol 'BinaryInteger' can only be used as a generic constraint because it has Self or associated type requirements
)
在general中,泛型在可能的情况下比存在型类型更可取,因为没有开销和更大的灵活性;但是,它们需要知道参数的静态类型,这并不总是可能的.
Existentials更容易接受输入,但在功能上明显更有限,而且是有成本的.
在更喜欢<T: P>
和some P
之间,或者P
和any P
之间--目前的 Select 是主观的,但:
- 作为SE-0335的一部分,目前计划SWIFT 6将使用关键字
any
来表示存在协议的使用;与使用该关键字对应的是some
,因此,如果您想要开始保护您的代码并保持一致性,开始迁移到any
和some
可能是个好主意
- 在不透明的参数语法和泛型语法之间, Select 由您自己决定,但不透明的参数目前不能涵盖泛型可以覆盖的所有用例,特别是具有更复杂的约束的用例.无论是在任何地方都坚持使用泛型,还是更喜欢不透明的参数并仅在必要时使用泛型,都是您需要做出的代码风格 Select