我在一个包含私有字段的包中有一个结构:
package foo
type Foo struct {
x int
y *Foo
}
另一个软件包(例如,白盒测试软件包)需要访问它们:
package bar
import "../foo"
func change_foo(f *Foo) {
f.y = nil
}
是否有一种方法可以声明bar
是某种“朋友”软件包,或者可以通过其他任何方式访问foo.Foo
的私有成员bar
,但仍然对所有其他软件包保持私有(也许在其中unsafe
)?
有 是 一种方法来 读取 使用反映不导出成员
func read_foo(f *Foo) {
v := reflect.ValueOf(*f)
y := v.FieldByName("y")
fmt.Println(y.Interface())
}
但是,尝试使用y.Set或通过其他方式设置反射字段将导致代码恐慌,您试图在软件包外部设置未导出的字段。
简而言之:未导出的字段出于某种原因应被取消导出,如果您需要更改它们,或者将需要更改的内容放在同一包中,或者公开/导出一些安全的更改方法。
也就是说,为了完全回答问题,您 可以 执行此操作
func change_foo(f *Foo) {
// Since structs are organized in memory order, we can advance the pointer
// by field size until we're at the desired member. For y, we advance by 8
// since it's the size of an int on a 64-bit machine and the int "x" is first
// in the representation of Foo.
//
// If you wanted to alter x, you wouldn't advance the pointer at all, and simply
// would need to convert ptrTof to the type (*int)
ptrTof := unsafe.Pointer(f)
ptrTof = unsafe.Pointer(uintptr(ptrTof) + uintptr(8)) // Or 4, if this is 32-bit
ptrToy := (**Foo)(ptrTof)
*ptrToy = nil // or *ptrToy = &Foo{} or whatever you want
}
这是一个非常非常糟糕的主意。它不是可移植的,如果进行大小更改,它将失败;如果您重新排列Foo中的字段顺序,更改其类型或大小,或在现有字段之前添加新字段,此功能将极大地更改无需告知您即可随意处理乱码数据的新表示形式。我也认为这可能会破坏此块的垃圾回收。
请,如果您需要从程序包外部更改字段,请编写功能以从程序包内部进行更改或将其导出。
编辑:这是一种较为安全的方法:
func change_foo(f *Foo) {
// Note, simply doing reflect.ValueOf(*f) won't work, need to do this
pointerVal := reflect.ValueOf(f)
val := reflect.Indirect(pointerVal)
member := val.FieldByName("y")
ptrToY := unsafe.Pointer(member.UnsafeAddr())
realPtrToY := (**Foo)(ptrToY)
*realPtrToY = nil // or &Foo{} or whatever
}
这样比较安全,因为它总是会找到正确的命名字段,但它仍然不友好,可能很慢,而且我不确定它是否与垃圾回收弄乱了。它也将无法警告你,如果你正在做一些奇怪的(你可以把这个代码
稍微 添加一些检查比较安全,但是我不会理会,这得到要点跨越不够好)。
Also keep in mind that FieldByName is susceptible to the package developer
changing the name of the variable. As a package developer, I can tell you that
I have absolutely no qualms about changing the names of things users should be
unaware of. You could use Field, but then you’re susceptible to the developer
changing the order of the fields with no warning, which is something I also
have no qualms about doing. Keep in mind that this combination of reflect and
unsafe is… unsafe, unlike normal name changes this won’t give you a compile
time error. Instead, the program will just suddenly panic or do something
weird and undefined because it got the wrong field, meaning even if YOU are
the package developer that did the name change, you still may not remember
everywhere you did this trick and spend a while tracking down why your tests
suddenly broke because the compiler doesn’t complain. Did I mention that this
is a bad idea?
Edit2:既然您提到了白盒测试,请注意,如果您在目录中命名文件,<whatever>_test.go
除非您使用go test
,否则它将不会编译,因此,如果您要进行白盒测试,请在顶部声明package <yourpackage>
,这将使您可以访问未导出的字段,如果您要做黑匣子测试,请使用package <yourpackage>_test
。
但是,如果您需要同时对两个软件包进行白盒测试,我认为您可能会陷入困境,可能需要重新考虑您的设计。
问题内容: 最近,我观察到在Java中访问priavte字段的意外行为。考虑以下示例,该示例说明了该行为: 为什么我可以访问类的其他对象的私有字段的内法(第二种情况)? 问题答案: 私有字段保护一个类,而不是实例。主要目的是允许一个类独立于其API实现。在它们之间隔离实例,或从相同类的静态代码中保护实例的代码都不会带来任何好处。
问题内容: 大家好!我有一个简单的问题。…为什么我可以从main方法中获得一个私有变量?我知道,我在包含类中,但这是主要的。我相信主体不是包含它的类的一部分……那么我不会去找私人成员,但是我可以……为什么?请帮助… thx 问题答案: Main是您的类的一部分,您已经在类中声明了它:) main不是您的对象的一部分,它不会是您从该类创建的对象的任何部分,但它仍然是该类的一部分。这对于任何静态函数都
问题内容: 请忘记设计。我知道OOP指定私有对象是该类的私有对象。我的问题是,为什么将OOP设计为使私有字段具有类级别的访问权限而不具有对象级别的访问权限? 问题答案: private修饰符强制执行封装原理。 这个想法是,“外部世界”不应更改Person内部流程,因为Person的实现可能会随时间而变化(并且你将不得不更改整个外部世界以解决实现方面的差异-这几乎是不可能的)。 当Person实例访
问题内容: 我正在学习Java,正在阅读的书中包含以下有关克隆的示例。在中,即使buffer是,我的第一个实例也可以在新对象上设置缓冲区。似乎应该要求该字段才能起作用。 为什么允许这样做?是否具有允许其访问字段的特殊特权? 问题答案: 该修改并不意味着只有同一个实例可以访问域; 这意味着只有相同类的对象才能访问它。 在Java语言规范说,在6.6节,访问控制: …如果成员或构造函数被声明为私有,则
问题内容: 在大学里学习时,我不得不做一些难看的Java基础知识,例如不使用封装就可以工作,同一类中的主要方法等。(我不想在Java样式指南上展开讨论,我只是想澄清一下,我不会在大学以外写这样的东西) 我偶然发现了一种我无法向自己解释的行为: 为什么这段代码可以编译并正确运行?我怎么可能访问私有字段?由于主类位于同一类中,因此行为异常? 问题答案: 由于静态方法是类的成员,因此可以访问中的任何私有
问题内容: 我有一个使用XML和反射将 s 返回到另一个类的类。 通常,这些对象是外部对象的子字段,但有时我想即时生成它。我已经尝试过类似的方法,但无济于事。我相信这是因为Java不允许你访问进行反射的方法。 如果提供的方法失败,则失败。我可以通过制作方法来解决它,或者制作另一个类来派生它。 长话短说,我只是想知道是否存在一种通过反射访问方法的方法。 问题答案: 你可以使用反射调用私有方法。修改已