golang any 之中的类型及 interface 接口

在 golang 之中 any 类型,从字面意思上看是任意类型,这很类似我们在 C#、C++ 之中的任意指针类型 void*(原生),C# 之中诡异的 object。

any 是一个接口类型,其语法声明为:

// 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 编译器上工作。

例如:

interface IFoo {
    void Say();
};

在 C++ 及 C# 之中,接口必须在具象类(Representational class)之中被显示派生才可以,它是基于对于 __vfptr 类虚函数表重写实现的。

当然与 Golang 相同,接口函数必须被具象类按照 “Function signature 函数签名” 实现才可以,但不同的是,Golang 之中不需要在 struct 之中声明派生具体的接口类型。

在 Golang 之中类型是否可以 “Covariations 协变” 为某个接口类型,只需要该类型实现,欲被协变的接口所需要的成员。

例如:

type IFoo interface {
	Say()
}

type FooImplement struct {
}

func (*FooImplement) Say() {
	fmt.Println("你好!")
}

func main() {
	var foo IFoo
	foo = &FooImplement{}

	foo.Say()
}

 举一反三:

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 指令拆箱的过程。

例如:

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)会发生一些很有意思的参数转发问题。

举个例子:

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到线程栈空间之中。

public void PrintNumbers(params object[] numbers)
{
    foreach (var number in numbers)
    {
        Console.WriteLine(number);
    }
}

所以在 golang 之中,如果人们需要转发类型为 ... any 的可变参数列表,应当:

func implement_print_args(a ...any) {
	fmt.Println(a...)
}

func forward_print_args(a ...any) {
	implement_print_args(a...)
}

但这并不仅仅是 any,定义任何类型的可变参数,都应当按照上述形式来声明函数及参数签名。

你可能感兴趣的:(Extension,golang,开发语言,后端)