Nie jest sposób czytać unexported członkowie użyciu odzwierciedlać
func read_foo(f *Foo) {
v := reflect.ValueOf(*f)
y := v.FieldByName("y")
fmt.Println(y.Interface())
}
jednak próbuje użyć y.Set, lub w inny sposób ustawić pole z odzwierciedlają spowoduje kodu panikować to ty” próbujesz ustawić nieodnotowane pole poza pakietem.
W skrócie: niewytrawione pola powinny być niezawarte z jakiegoś powodu, jeśli trzeba je zmienić, należy umieścić coś, co trzeba zmienić w tym samym opakowaniu, lub wyeksponować/wyeksportować w bezpieczny sposób, aby je zmienić.
Powiedział, że w interesie pełni odpowiedzieć na pytanie, może zrobić
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
}
jest to naprawdę zły pomysł. Nie jest przenośny, jeśli kiedykolwiek zmieni rozmiar, to się nie powiedzie, jeśli kiedykolwiek zmienisz kolejność pól w Foo, zmienisz ich typy lub ich rozmiary, lub dodasz nowe pola przed wcześniejszymi, funkcja ta będzie wesoło zmieniać nowa reprezentacja przypadkowych danych z bełkotu bez informowania o tym. Myślę też, że może przerwać wyrzucanie śmieci dla tego bloku.
Jeśli chcesz zmienić pole spoza paczki, zapisz funkcję, aby zmienić ją z poziomu paczki lub wyeksportować.
Edycja: Oto nieco bezpieczniejszy sposób, aby to zrobić:
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
}
Jest to bezpieczniejsze, ponieważ będzie ona zawsze znaleźć odpowiednie pole o nazwie, ale to nadal nieprzyjazny, prawdopodobnie powolny, i nie jestem pewien, jeśli komunikuje się ze zbieraniem śmieci. Nie ostrzeże Cię również, jeśli robisz coś dziwnego (możesz uczynić ten kod bezpieczniejszym, dodając kilka sprawdzeń, ale nie będę się tym przejmował, to będzie dobrze zrozumiałe).
Należy również pamiętać, że nazwa FieldByName jest podatna na zmianę nazwy zmiennej przez programistę pakietów. Jako programista pakietów mogę powiedzieć, że nie mam absolutnie żadnych skrupułów, by zmienić nazwy rzeczy, których użytkownicy powinni być nieświadomi. Możesz użyć Field, ale wtedy jesteś podatny na dewelopera, zmieniając kolejność pól bez ostrzeżenia, co też nie mam zastrzeżeń. Należy pamiętać, że ta kombinacja refleksji i niebezpieczeństwa jest ... niebezpieczna, w przeciwieństwie do zwykłych zmian nazw, nie spowoduje to błędu podczas kompilacji. Zamiast tego program nagle wpadnie w panikę lub zrobi coś dziwnego i nieokreślonego, ponieważ ma niewłaściwe pole, co oznacza, że nawet jeśli TY jesteś deweloperem pakietów, który zmienił nazwę, nadal możesz nie pamiętać wszędzie, gdzie zrobiłeś tę sztuczkę i spędzić trochę czasu na śledzeniu dlaczego testy nagle się zepsuły, ponieważ kompilator nie narzeka.Czy wspomniałem, że to zły pomysł?
Edit2: Skoro wspomniałeś testowanie białe pole, pamiętać, że jeśli nazwa pliku w katalogu <whatever>_test.go
nie będzie kompilować, chyba że używasz go test
, więc jeśli chcesz zrobić biały testowanie, na szczycie której deklarują package <yourpackage>
da Ci dostęp do nieodnotowanych pól, a jeśli chcesz wykonać test czarnej skrzynki, użyj package <yourpackage>_test
.
Jeśli chcesz przetestować dwie paczki w tym samym czasie, myślę, że możesz zostać zablokowany i może być konieczne ponowne przemyślenie projektu.
Nie można rozwidlić istniejącej biblioteki i odsłonić pola, które należy zmodyfikować? (zwróć uwagę, że powinieneś założyć, że są niewystawione z jakiegoś powodu) – elithrar
@elithrar Wszystko to jest mój kod. Więc ... tak, są nienaświetleni z ważnego powodu; i tak, potrzebuję do nich dostępu. – Matt