当前位置: 首页 > 面试题库 >

以匿名结构作为参数导出函数[在package.Func的参数中不能将值(结构{…}类型)用作结构{…}类型]

邴奇逸
2023-03-14
问题内容

这是一段[play.google.org](https://play.golang.org/p/nk9HBJAMO3)可以毫无问题运行的代码:

package main

import (
    "fmt"
)

func PrintAnonymous(v struct {
    i int
    s string
}) {
    fmt.Printf("%d: %s\n", v.i, v.s)
}

func PrintAnonymous2(v struct{}) {
    fmt.Println("Whatever")
}

func main() {
    value := struct {
        i int
        s string
    }{
        0, "Hello, world!",
    }
    PrintAnonymous(value)
    PrintAnonymous2(struct{}{})
}

但是,如果该PrintAnonymous()函数存在于另一个包中(例如temp),则该代码将不起作用:

cannot use value (type struct { i int; s string })
as type struct { i int; s string } in argument to temp.PrintAnonymous

我的问题是:

  • 有没有一种方法可以使用匿名结构作为参数来调用(公共)函数(又名PrintAnonymous()上文)?
  • 以空struct作为参数的函数(也称为PrintAnonymous2()上文)可以被调用,即使它存在于另一个包中。这是特例吗?

好吧,我知道我总是可以命名struct来解决问题,我对此感到很好奇,并且想知道为什么似乎不允许这样做。


问题答案:

您的匿名结构类型的字段未导出。这意味着您无法创建该结构的值,也无法为另一个包中的字段指定值。规格:复合文字:

为属于不同包的结构的非导出字段指定元素是错误的。

如果您更改结构定义以导出字段,那么它将起作用,因为所有字段都可以由其他程序包分配(请参见Siu Ching Pong -Asuka Kenji-的答案)。

空结构(也没有字段)也是如此:空结构没有字段,因此没有未导出的字段,因此您可以传递该值。

但是,您可以通过反射使用未经修改的结构(具有未导出的字段)来调用该函数。你可以得到reflect.Type的的PrintAnonymous()功能,并且可以使用Type.In()来获取reflect.Type它的第一个参数。那就是我们要传递值的匿名结构。您可以使用构造该类型的值reflect.New()。这将是一个reflect.Value,包装指向匿名结构零值的指针。抱歉,您的结构值不能包含非零值的字段(由于上述原因)。

它看起来像这样:

v := reflect.ValueOf(somepackage.PrintAnonymous)
paramt := v.Type().In(0)
v.Call([]reflect.Value{reflect.New(paramt).Elem()})

这将打印:

0:

0是的零值int,和的""空字符串string

有趣的是( 这是一个错误
,请参见下面的链接问题),通过反射,您可以使用自己的匿名结构类型的值(具有匹配的未导出字段),在这种情况下,我们可以使用除0之外的其他值。结构域:

value := struct {
    i int
    s string
}{
    1, "Hello, world!",
}

v.Call([]reflect.Value{reflect.ValueOf(value)})

高于跑步次数(无需惊慌):

1: Hello, world!

允许这样做的原因是由于编译器中的错误。请参见下面的示例代码:

s := struct{ i int }{2}

t := reflect.TypeOf(s)
fmt.Printf("Name: %q, PkgPath: %q\n", t.Name(), t.PkgPath())
fmt.Printf("Name: %q, PkgPath: %q\n", t.Field(0).Name, t.Field(0).PkgPath)

t2 := reflect.TypeOf(subplay.PrintAnonymous).In(0)
fmt.Printf("Name: %q, PkgPath: %q\n", t2.Name(), t2.PkgPath())
fmt.Printf("Name: %q, PkgPath: %q\n", t2.Field(0).Name, t2.Field(0).PkgPath)

输出为:

Name: "", PkgPath: ""
Name: "i", PkgPath: "main"
Name: "", PkgPath: ""
Name: "i", PkgPath: "main"

如您所见i,两种匿名结构类型中的未导出字段(在main包中以及somepackage作为PrintAnonymous()函数的参数)–错误地–报告相同的包,因此它们的类型将相等:

fmt.Println(t == t2) // Prints true

注意:我认为这是一个 缺陷
:如果允许使用反射,则应该也可以不使用反射。如果在没有反射的情况下证明了编译时错误,那么使用反射会导致运行时出现恐慌。我为此打开了一个问题,您可以在此处关注它:Issue#16616。Fix当前针对Go1.8。



 类似资料:
  • 你能以通常做法把结构作为参数传递。例如: void printPoint ( Point p) { cout << "(" << p.x << ", " << p.y << ")" << endl; } printPoint方法把一个point作为参数,并以标准格式将其输出。若调用printPoint(blank),则会输出(3,4)。 作为第二个例子,可重写5.2节的distance函

  • 像inet_ntop、WSAAddressToString、recvfrom这样的函数还需要提供实际sockaddr结构或地址族的大小,这有什么意义? sockaddr结构的前两个字节表示地址族,因此也表示它实际上是sockaddr_in还是sockaddr_in 6。 那么,增加大小(sockaddr_in/sockaddr_in 6)和AF_INET/AF_INET6的原因是什么?

  • 我正在使用一个第三方库函数,它有大量的位置参数和命名参数。在我的代码中,从多个点使用相同的参数/值调用该函数。 为了便于维护,我不想在代码中多次硬编码几十个相同的参数。我希望有一种方法可以在数据结构中存储它们一次,所以我只需要传递数据结构。按照以下思路: 假设我调用的函数的签名如下: 假设在我的代码中,我想用 for arg1, for arg2 for arg4 (而且我没有使用arg3)。 我

  • 本文向大家介绍C++动态分配和撤销内存以及结构体类型作为函数参数,包括了C++动态分配和撤销内存以及结构体类型作为函数参数的使用技巧和注意事项,需要的朋友参考一下 C++动态分配内存(new)和撤销内存(delete) 在软件开发过程中,常常需要动态地分配和撤销内存空间,例如对动态链表中结点的插入与删除。在C语言中是利用库函数malloc和free来分配和撤销内存空间的。C++提供了较简便而功能较

  • 问题内容: http://play.golang.org/p/vhaKi5uVmm [第一个问题] 我们如何以及为什么需要这种看起来很奇怪的结构?它是空结构还是匿名结构?我用谷歌搜索,但是找不到正确的答案或说明文档。 原始资料来自Andrew Gerrand的演讲 http://nf.wh3rd.net/10things/#10 这里 完成是struct {}类型的通道 所以我尝试了 但这是行不通

  • 当你创建一个对象并给它赋一个变量的时候,这个变量仅仅 参考 那个对象,而不是表示这个对象本身!也就是说,变量名指向你计算机中存储那个对象的内存。这被称作名称到对象的绑定。 一般说来,你不需要担心这个,只是在参考上有些细微的效果需要你注意。这会通过下面这个例子加以说明。 对象与参考 例9.6 对象与参考 #!/usr/bin/python # Filename: reference.py print