Es ist ein Weg, um lesen unexported Mitglieder
func read_foo(f *Foo) {
v := reflect.ValueOf(*f)
y := v.FieldByName("y")
fmt.Println(y.Interface())
}
jedoch versuchen y.Set zu verwenden oder auf andere Weise setzen Sie das Feld reflektieren Verwendung mit in dem Code, den Sie in Panik zu geraten führt reflektieren‘ Versuchen Sie, ein nicht exportiertes Feld außerhalb des Pakets zu setzen.
Kurz gesagt: Nicht exportierte Felder sollten aus einem bestimmten Grund nicht exportiert werden. Wenn Sie sie ändern müssen, legen Sie entweder das Objekt in das gleiche Paket oder exponieren/exportieren Sie einen sicheren Weg, um es zu ändern.
Das heißt, im Interesse der Frage vollständig zu beantworten, Sie können tun, um diese
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
}
Dies ist ein wirklich, wirklich schlechte Idee. Es ist nicht portierbar, wenn die Größe jemals geändert wird, wird es scheitern, wenn Sie die Reihenfolge der Felder in Foo ändern, ihre Typen oder ihre Größe ändern oder neue Felder vor den bereits bestehenden hinzufügen, wird diese Funktion die Änderungen munter ändern neue Darstellung zu zufälligen Kauderwelsch-Daten ohne es Ihnen zu sagen. Ich denke auch, dass es Müllsammlung für diesen Block brechen könnte.
Wenn Sie ein Feld außerhalb des Pakets ändern müssen, schreiben Sie entweder die Funktionalität, um es im Paket zu ändern, oder exportieren Sie es.
Edit: Hier ist ein wenig sicherer Weg, es zu tun:
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
}
Dies ist sicherer, da es immer den richtigen Namen Feld finden, aber es ist immer noch unfreundlich, wahrscheinlich langsam, und ich bin nicht sicher, wenn es mit der Müllsammlung zu tun hat. Es wird Sie auch nicht warnen, wenn Sie etwas komisches machen (Sie könnten diesen Code zu einem kleinen sicherer machen, indem Sie ein paar Checks hinzufügen, aber ich werde mich nicht darum kümmern, das bringt den Kern gut genug).
Denken Sie auch daran, dass FieldByName für den Paketentwickler anfällig ist, den Namen der Variablen zu ändern. Als Paketentwickler kann ich Ihnen sagen, dass ich absolut keine Bedenken habe, die Namen von Dingen zu ändern, die den Benutzern nicht bewusst sein sollten. Sie könnten Field verwenden, aber dann sind Sie anfällig dafür, dass der Entwickler die Reihenfolge der Felder ohne Warnung ändert, was ich auch nicht befürchte. Denken Sie daran, dass diese Kombination von "reflect" und "unsafe" nicht sicher ist. Anders als bei normalen Namensänderungen erhalten Sie dadurch keinen Fehler bei der Kompilierung. Stattdessen wird das Programm plötzlich in Panik geraten oder etwas Seltsames und Undefiniertes tun, weil es das falsche Feld hat. Das heißt, selbst wenn du der Paketentwickler bist, der den Namen geändert hat, erinnerst du dich vielleicht nicht überall wo du diesen Trick gemacht hast wieso sind deine Tests plötzlich kaputt, weil der Compiler sich nicht beschwert.Habe ich erwähnt, dass dies eine schlechte Idee ist?
Edit2: Da Sie White Box Tests erwähnen, beachten Sie, dass, wenn Sie eine Datei in Ihrem Verzeichnis <whatever>_test.go
Namen wird es nicht kompilieren, wenn Sie go test
verwenden, wenn Sie also weißen Box-Test machen wollen, an der Spitze package <yourpackage>
erklären die gibt Ihnen Zugriff auf nicht-exportierte Felder, und wenn Sie Black-Box-Tests durchführen möchten, verwenden Sie package <yourpackage>_test
.
Wenn Sie in der gleichen Zeit zu White-Box-Test zwei Pakete benötigen, jedoch denke ich, dass Sie nicht mehr sein können, und müssen möglicherweise Ihr Design zu überdenken.
Können Sie die vorhandene Bibliothek nicht forchieren und die Felder offen legen, die Sie ändern müssen? (Beachten Sie, dass Sie davon ausgehen sollten, dass sie aus einem guten Grund unbelichtet sind) – elithrar
@elithrar Alles ist mein Code. Also ... ja, sie sind aus einem guten Grund nicht ausgesetzt; und ja, ich muss auf sie zugreifen. – Matt