golang any 之中的类型及 interface 接口

在 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,定义任何类型的可变参数,都应当按照上述形式来声明函数及参数签名。

相关推荐
夏子曦1 小时前
C#——NET Core 中实现汉字转拼音
开发语言·c#
꧁坚持很酷꧂2 小时前
Qt天气预报系统绘制温度曲线
开发语言·qt
电商数据girl2 小时前
【Python爬虫电商数据采集+数据分析】采集电商平台数据信息,并做可视化演示
java·开发语言·数据库·爬虫·python·数据分析
海尔辛2 小时前
学习黑客Bash 脚本
开发语言·学习·bash
源码云商2 小时前
基于 SpringBoot + Vue 的校园管理系统设计与实现
vue.js·spring boot·后端
小白学大数据3 小时前
分布式爬虫去重:Python + Redis实现高效URL去重
开发语言·分布式·爬虫·python
奔驰的小野码3 小时前
SpringAI实现AI应用-内置顾问
java·人工智能·后端·spring
普通人zzz~3 小时前
SpringBoot记录用户操作日志
java·spring boot·后端
大三开学菜鸟3 小时前
记录一下spring-cloud-starter-alibaba-nacos-config 2023.0.3.2与springboot版本及配置问题
java·spring boot·后端·spring·intellij-idea
可可乐不加冰3 小时前
QT生成保存 Excel 文件的默认路径,导出的文件后缀自动加(1)(2)等等
开发语言·qt