英文:
What don't structs created with a initilzer function get exported to test packages?
问题
假设我们有一个结构体和一个用于该结构体的构造函数,代码如下:
package myPackage
type Client struct {
a TypeA
b TypeB
}
func NewClient(a TypeA, b TypeB) ConcreteClient {
return &Client{
a: a,
b: b,
}
}
type ConcreteClient interface {
ExportedFunc()
}
func (c *Client) privateFunc() {
// ...
}
func (c *Client) ExportedFunc() {
// ...
}
我们在一个测试包中使用这个客户端,代码如下:
var (
c = &Client{
a: a,
b: b,
}
)
func TestUnexported(t *testing.T) {
c.privateFunc() // 可以正常工作
}
在上述情况下,私有方法在测试文件中被正常发现。但是,当我们像这样使用构造函数时:
var (
c = NewClient()
)
func TestUnexported(t *testing.T) {
c.privateFunc() // 无法工作
}
私有方法不会被暴露给测试。这些文件存在于同一个包中,并遵循*_test
的命名模式。我还没有完全弄清楚发生了什么作用域方面的问题,为什么通过构造函数创建的私有方法会被隐藏,而通过常规构造方法则不会。
英文:
Say we have a struct and a constructor function for the structure like such
package myPackage
type Client struct {
a TypeA
b TypeB
}
func NewClient(a TypeA, b TypeB) ConcreteClient {
return &Client{
a: a,
b: b,
}
}
type ConcreteClient interface {
ExportedFunc()
}
func (c *Client) privateFunc() {
// ...
}
func (c *Client) ExportedFunc() {
// ...
}
And we use this client in a test package like such
var (
c = &Client {
a:a,
b:b,
}
)
func TestUnexported(t *testing.T) {
c.privateFunc() // Works
}
In the previous case the unexported is discovered as expected in the test file but when we use the constructor like this
var (
c = NewClient()
)
func TestUnexported(t *testing.T) {
c.privateFunc() // Doesn't work
}
The unexported method isn't exposed for testing. These files exist within the same package and follow the *_test naming pattern.I haven't been able to figure out exactly what is going on scoping wise as to why the unexported methods are hidden when created through a constructor and not through typical construction.
- Edited for clarity
答案1
得分: 1
当你将一个类型作为接口返回或在函数中接受一个接口时,该类型将被缩减为仅包含该接口中的方法,其他所有内容将无法访问。你已经有效地将类型从Client
转换为ConcreteClient
。
考虑一下,如果所有类型的所有方法都可以轻松访问,代码会是什么样子:
func f(fp io.Reader) {
fp.Seek(42)
}
这样的函数使用起来会很烦人:“你可以传递任何io.Reader
给它,但是哦,它还必须有Seek()
方法,否则你会得到编译错误”。
这就是为什么会存在额外的接口,比如io.ReadSeeker
(它具有Read()
和Seek()
方法)。
并非一切都失去了,你仍然可以通过类型的方法访问一切:
func (c *Client) ExportedFunc() string {
return c.a
}
func main() {
cclient := NewClient("this is a", "this is b")
fmt.Printf(cclient.ExportedFunc())
}
或者你可以使用类型断言来获取原始类型:
cclient := NewClient("this is a", "this is b")
client, ok := cclient.(*Client)
if !ok {
fmt.Printf("not a Client: %T\n", cclient)
os.Exit(1)
}
fmt.Printf("a: %v\n", client.a)
英文:
When you return a type as an interface or accept an interface in a function, then the type is reduced to only the methods in that interface and everything else will be inaccessible. You've effective "converted" the type from Client
to ConcreteClient
.
Consider how the code would look if all methods from all types be readily accessible:
func f(fp io.Reader) {
fp.Seek(42)
}
How annoying would such a function be to use: "you can pass any io.Reader
to this, but oh, it must also have the Seek()
method or you'll get compile errors".
This is why additional interfaces such as io.ReadSeeker
(which has Read()
and Seek()
) exist.
Not all is lost, you can still use access everything from the type methods:
func (c *Client) ExportedFunc() string {
return c.a
}
func main() {
cclient := NewClient("this is a", "this is b")
fmt.Printf(cclient.ExportedFunc())
}
Or you can use a type assertion to get the original type back:
cclient := NewClient("this is a", "this is b")
client, ok := cclient.(*Client)
if !ok {
fmt.Printf("not a Client: %T\n", cclient)
os.Exit(1)
}
fmt.Printf("a: %v\n", client.a)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论