结构体成员在作为参数传递时的作用域可见性是怎样的?

huangapple go评论109阅读模式
英文:

Scope visibility of struct members while passing as a argument?

问题

在调用json.Decoder.Decode时,通过传递一个结构体,例如:

type _Sample struct {
    first string // 这个字段不会被填充,因为它以小写字母开头
    Second string // 这个字段是可以的
}

...
var sample _Sample
err := decoder.Decode(&sample)

根据语言规范的说明:

导出的标识符:

为了允许从另一个包中访问标识符,可以将其导出。如果满足以下两个条件,则标识符被导出:

  • 标识符的名称的第一个字符是一个Unicode大写字母(Unicode类别“Lu”);
  • 标识符在包块中声明,或者它是字段名或方法名。

所有其他标识符都不会被导出。

问题是_Sample结构体并没有被导出,为什么它可以被json包访问到呢?

英文:

While calling json.Decoder.Decode by passing a struct, for instance

type _Sample struct {
    first string // this will not be filled because it starts with lower case letter
    Second string // it is OK. 
}

...
var sample _Sample
err := decoder.Decode(&sample)

According to the Language Specification that writes:

> Exported identifiers ¶
>
> An identifier may be exported to permit access to it from another package. An identifier is exported if both:
>
> - the first character of the identifier's name is a Unicode upper case letter (Unicode class "Lu"); and
> - the identifier is declared in the package block or it is a field name or method name.
>
>
> All other identifiers are not exported.

The question is the struct _Sample is not exported, why it is visible by the package json?

答案1

得分: 3

访问权限 在以下情况下进行检查:

  1. 当你创建/声明一个变量时,编译器会检查类型标识符的名称的可见性。
  2. 使用 reflect 包来修改一个变量。
  3. 调用一个常量、全局变量或函数。将检查常量/变量/函数名称的可见性。

一旦变量被创建/定义,并且当你将这个变量作为函数/方法的参数传递时,副本(如果是值类型,则复制值;如果是指针类型,则复制地址)将被传递给函数/方法,并且始终可以通过参数名称和类型从函数体中访问。在 encoding/json 包中,Decoder.Decode 方法被定义为:

func (dec *Decoder) Decode(v interface{}) error

因此,你传递给 Decoder.Decode 方法的任何内容都可以通过参数 v 在方法体内部访问。请注意,v 的类型是 interface{} 而不是 _Sample 结构体。可见性在你定义变量时进行检查,例如:

var sample _Sample

这是可以的,因为它是在同一个包内部完成的。甚至匿名结构体(即结构体没有定义类型名称标识符)也可以正常工作,例如:

aSample := struct {
	first  string // 这个字段不会被填充,因为它以小写字母开头
	Second string // 这是可以的
}{}

//...
err := decoder.Decode(&aSample)

接下来,当 decoder 填充结构体(通过反射),将会检查结构体成员的可见性,而 _Sample.firstjson 包内部是不可见的。

英文:

Access permission is checked when

  1. You're creating/declaring a variable. The compiler will check the visibility of the name of the type identifier.
  2. Using reflect package to modify a variable.
  3. Calling a constant, global variable, or a function. The visibility of constant/variable/function name will be checked.

Once the variable is created/defined, and when you pass this variable as a function/method argument, the copy (If it is a value, the value is being copied. If it is a pointer, the address is being copied) will be passed to the function/method, and always accessible from the function body through the argument name and type. In encoding/json package, the Decoder.Decode method is defined as,

func (dec *Decoder) Decode(v interface{}) error

thus, anything you passed to the Decoder.Decode method, is always accessible from inside the method body through argument v. Note that the type of v is interface{} not _Sample struct. The visibility is checked when you defined the variable as

var sample _Sample

which was OK since it is done from within the same package. Even an anonymous struct (i.e. no type name identifier is defined for the struct)

aSample := struct {
	first  string // this will not be filled because it starts with lower case letter
	Second string // it is OK.
}{}

//...
err := decoder.Decode(&aSample)

will works. Next, when the decoder fills the struct (through reflection), the visibility of struct members will be checked, and _Sample.first is not visible from inside the json package.

huangapple
  • 本文由 发表于 2017年6月21日 10:19:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/44665834.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定