对于通用的自由函数,我可以使用重载来将函数针对函数类型进行专门化,例如:
func foo<T>(_ t: T.Type) { print("T is unknown") }
func foo<P>(_ t: ((P) -> Void).Type) { print("T is a function with one parameter") }
let f: (String) -> Void = { print($0) }
foo(type(of: f)) // prints "T is a function with one parameter"
注意,
foo()
的第二个版本并没有遵循协议约束,主要是因为据我所知,我们无法使函数类型符合协议(我们无法扩展非命名类型)。我可以创建一个 OneParamFunction
协议,并在受限制的 foo()
中使用它,但我不能让所有单参数函数类型都符合该协议。但上述重载可以在没有协议约束的情况下工作。
对于泛型类的实例方法,是否有类似这样的解决方案?
对我来说,这种语法似乎最自然,但不被支持:
class Generic1<T> { init(_ t: T.Type) {} }
extension Generic1 { func foo() { print("T is unknown") } }
extension Generic1<P>
where T == ((P) -> Void) {
func foo() { print("T is a function with one parameter") }
}
“正常”创建泛型类协议限制扩展的方式如下所示:
extension Generic1 where T: OneParamFunction { ... }
但正如上面讨论的那样,我不能使函数类型符合OneParamFunction协议。
我也不能只创建一个单一的(无重载/特化)实例方法,然后转发到自由函数,这不起作用:
class Generic2<T> {
init(_ t: T.Type) {}
func foo() { myModule.foo(T.self) }
}
let f: (String) -> Void = { print($0) }
Generic2(type(of: f)).foo() // prints "unknown T"
代码可以编译,但总是调用未知的T版本,我认为是由于类型擦除。
在Generic2内部,编译器不知道T是什么。
Generic2没有对T定义任何协议约束,这会帮助编译器正确派发myModule.foo()
调用(并且它不能具有这样的约束,请参见上文)。
在泛型类内使用方法重载编译似乎很接近解决问题, 但仍然无法工作,尽管在这种情况下我不确定原因。
class Generic3<T> {
init(_ t: T.Type) {}
func foo() { print("T is unknown") }
func foo<P>() where T == ((P) -> Void) { print("T is a function with one parameter") }
}
let f: (String) -> Void = { print($0) }
Generic3(type(of: f)).foo() // prints "unknown T"
在调用
foo()
的地方, Generic3的类型参数已经完全确定,所以我认为编译器应该具备所有必要的类型信息来正确地分派调用,但实际情况并非如此,它仍然打印出"unknown T"。
甚至将类型作为foo()
方法的参数重复也没有帮助(也不理想)。class Generic4<T> {
init(_ t: T.Type) {}
func foo(_ t: T.Type) { print("T is unknown") }
func foo<P>(_ t: T.Type) where T == ((P) -> Void) { print("T is a function with one parameter") }
}
let f: (String) -> Void = { print($0) }
Generic4(type(of: f)).foo(type(of: f)) // still prints "unknown T"
我还有其他选择吗?
更新,针对Rob Napier的回答。
我想我在这里希望实现的不是动态派发,而是静态派发,但是基于调用点所知道的所有类型信息,而不是基于在Generic.init()
期间之前推断出的T
的类型擦除值。这适用于自由函数,但不适用于成员函数。
试一下这个:
func foo<T>(_ t: T.Type) { print("T is unknown") }
func foo<P>(_ t: ((P) -> Void).Type) { print("T is a function with one parameter") }
func g<T>(_ x: T.Type) -> T.Type { return x }
let f: (String) -> Void = { print($0) }
foo(g(type(of: f))) // prints "T is a function"
这确实会调用
foo
的“T is function”版本,尽管T
在g()
中也被类型擦除了。我认为这更类似于Generic(type(of: f)).foo()
,而不是Rob示例中使用g<T>()
调用foo()
(这更类似于从Generic
的其他成员调用Generic.foo()
-- 在这种情况下,我理解为什么T
是未知的)。在两种情况下(
Generic(type(of: f)).foo()
vs foo(g(type(of: f)))
),有两种类型:
1. f
的原始类型,以及
2. 第一次调用(Generic.init()
/ g()
)返回的类型。但显然,对于调用自由函数
foo()
,后续的foo()
调用是基于类型#1进行分派,而对于调用成员函数Generic.foo()
,则使用类型#2进行分派。起初我以为差异与上面的示例中
g()
返回T.Type
,而Generic.init()
的结果是Generic<T>
有关,但实际不是这样。class Generic_<T> {
init(_ t: T.Type) {}
func member_foo() { print("T is unknown") }
func member_foo<P>() where T == ((P) -> Void) { print("T is a function with one parameter") }
}
func free_foo<T>(_ g: Generic_<T>) { print("T is unknown") }
func free_foo<P>(_ t: Generic_<(P) -> Void>) { print("T is a function with one parameter") }
func g_<T>(_ t: T.Type) -> Generic_<T> { return Generic_(t) }
free_foo(g_(type(of: f))) // T is function
Generic_(type(of: f)).member_foo() // T is unknown
在这种情况下,
Generic.init
和g()
都返回Generic<T>
。然而,free_foo()
调用似乎是基于f
的完整原始类型进行分派的,而member_foo()
调用则不是。我仍然在想为什么。
member_foo<P>()
函数没有参数来帮助编译器推断P
,因为我们已经知道它不支持“解构”函数类型。但是我想我找到了一种方法来编写它,以便它打印出你想要的内容。我会告诉你的 ;) - AnderCover