在 golang 之中 any 类型,从字面意思上看是任意类型,这很类似我们在 C#、C++ 之中的任意指针类型 void*(原生),C# 之中诡异的 object。
any 是一个接口类型,其语法声明为:
Go
// any is an alias for interface{} and is equivalent to interface{} in all ways.
type any = interface{}
即 interface{} 等于 any,这是一种类似 C++ 之中语法为:
using 别名 = 类型;
别名定义方式,C# 这块只允许为命名空间定义别名,就像在 C++ 使用 namespace 别名 = 命名空间; 类似这样子。
Golang 与 C/C++、C# 这样的语言是不同的,在 VC++ 之中微软提供了 __interface 关键字,将 C/C++ 混乱不堪的定义 Abstract class 为强行理解为 interface 行为给取缔,但可惜的是:它只被允许在 Microsoft VC++ 、CL 编译器上工作。
例如:
cpp
interface IFoo {
void Say();
};
在 C++ 及 C# 之中,接口必须在具象类(Representational class)之中被显示派生才可以,它是基于对于 __vfptr 类虚函数表重写实现的。
当然与 Golang 相同,接口函数必须被具象类按照 "Function signature 函数签名" 实现才可以,但不同的是,Golang 之中不需要在 struct 之中声明派生具体的接口类型。
在 Golang 之中类型是否可以 "Covariations 协变" 为某个接口类型,只需要该类型实现,欲被协变的接口所需要的成员。
例如:
Go
type IFoo interface {
Say()
}
type FooImplement struct {
}
func (*FooImplement) Say() {
fmt.Println("你好!")
}
func main() {
var foo IFoo
foo = &FooImplement{}
foo.Say()
}
举一反三:
cpp
type IFoo interface{}
type FooImplement struct{}
func (*FooImplement) Say() {
fmt.Println("你好!")
}
func test_foo(foo IFoo) {
}
func test_any(v any) {
test_foo(v)
}
func main() {
foo := &FooImplement{}
test_any(foo)
}
如上所示:
所以:当用户 interface 被定义为空集时,它与 interface {} 或者说 any 类型(别名)是等价的,可以无障碍的相互传递。
注意:
Golang 接口只可定义接口函数,但C#、VB.NET、C++ .NET 可以允许定义,如接口成员属性、成员事件等。
当 interface 接口类型想要 "Contravariants 逆变" 为具体类型的时候,这个过程人们可以想象为一种 unbox 指令拆箱的过程。
例如:
cpp
type IFoo interface {
Say()
}
type FooImplement struct{}
func (*FooImplement) Say() {
fmt.Println("你好!")
}
func test_foo(foo IFoo) {
f, ok := foo.(*FooImplement)
if ok {
f.Say()
fmt.Println("拆箱成功!")
} else {
fmt.Println("拆箱失败!")
}
}
func main() {
foo := &FooImplement{}
test_foo(foo)
}
在 Golang 之中 any 类型是一个很奇怪的东西,如果我们声明某个函数为 ... any 可变参数类型(any)会发生一些很有意思的参数转发问题。
举个例子:
Go
func implement_print_args(a ...any) {
fmt.Println(a...)
}
func forward_print_args(a ...string) {
implement_print_args(a...)
}
上述代码是无法编译通过的,从人类易于理解的角度来说,any 类型的可变参数,应该是可以接受任何类型的,这也应当包含 string 类型。
但奇怪的是 forward_print_args 函数,根本无法把自己的可变字符串类型参数 a,转发给 implement_print_args 函数。
这是因为,在 Golang 语言之中,any 的确可以等于任何类型,但在不等于它不存在限制,另外在 Golang 之中的可变参数是像 C# 语言之中使用一个 object[] 数组来模拟的可变参数。
人们稍需注意一点,Golang 并非是像 C/C++ 语言之中,真正意义上的可变参数,即根据函数调用协议(如 __cdecl、__stdcall、__fastcall、__thiscall、__pascal)及平台来决定那些参数压入到寄存器,如RDX、RCX、那些参数PUSH到线程栈空间之中。
Go
public void PrintNumbers(params object[] numbers)
{
foreach (var number in numbers)
{
Console.WriteLine(number);
}
}
所以在 golang 之中,如果人们需要转发类型为 ... any 的可变参数列表,应当:
Go
func implement_print_args(a ...any) {
fmt.Println(a...)
}
func forward_print_args(a ...any) {
implement_print_args(a...)
}
但这并不仅仅是 any,定义任何类型的可变参数,都应当按照上述形式来声明函数及参数签名。