Có là một cách để đọc thành viên unexported sử dụng phản ánh
func read_foo(f *Foo) {
v := reflect.ValueOf(*f)
y := v.FieldByName("y")
fmt.Println(y.Interface())
}
Tuy nhiên, cố gắng sử dụng y.Set, hoặc nếu không thiết lập hiện trường với phản ánh sẽ cho kết quả trong các mã hoảng loạn mà bạn' đang cố gắng thiết lập một trường chưa được công bố bên ngoài gói.
Tóm lại: các trường chưa được công bố sẽ không được báo cáo vì lý do, nếu bạn cần thay đổi chúng, hãy đặt thứ cần thay đổi trong cùng một gói hoặc hiển thị/xuất một số cách an toàn để thay đổi nó.
Điều đó nói rằng, vì lợi ích của việc trả lời đầy đủ các câu hỏi, bạn có thể làm điều này
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
}
Đây là một ý tưởng thực sự, thực sự xấu. Nó không phải là di động, nếu bạn thay đổi kích thước, nó sẽ thất bại, nếu bạn sắp xếp thứ tự các trường trong Foo, thay đổi kiểu của chúng, hoặc kích thước của chúng hoặc thêm trường mới trước những trường đã tồn tại từ trước, chức năng này sẽ thay đổi đại diện mới cho dữ liệu ngẫu nhiên vô nghĩa mà không nói cho bạn biết. Tôi cũng nghĩ rằng nó có thể phá vỡ bộ sưu tập rác cho khối này.
Vui lòng, nếu bạn cần thay đổi trường từ bên ngoài gói hoặc viết chức năng để thay đổi từ trong gói hoặc xuất nó.
Edit: Đây là một cách hơi an toàn hơn để làm điều đó:
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
}
Đây là an toàn hơn, vì nó sẽ luôn tìm ra tên trường đã chính xác, nhưng nó vẫn không thân thiện, có lẽ chậm, và tôi không chắc chắn nếu nó lộn xộn với bộ sưu tập rác. Nó cũng sẽ không cảnh báo bạn nếu bạn đang làm điều gì đó kỳ quặc (bạn có thể làm cho mã này trở thành một số nhỏ hơn an toàn hơn bằng cách thêm một vài kiểm tra, nhưng tôi sẽ không bận tâm, điều này có được ý chính cũng đủ).
Cũng xin lưu ý rằng FieldByName dễ bị nhà phát triển gói thay đổi tên của biến. Là một nhà phát triển gói, tôi có thể nói với bạn rằng tôi hoàn toàn không cảm thấy lo lắng về việc thay đổi tên của những thứ mà người dùng không biết. Bạn có thể sử dụng Field, nhưng sau đó bạn dễ bị nhà phát triển thay đổi thứ tự của các lĩnh vực mà không có cảnh báo, đó là một cái gì đó tôi cũng không có khiếu nại về làm. Hãy nhớ rằng sự kết hợp của phản ánh và không an toàn này là ... không an toàn, không giống như các thay đổi tên thông thường, điều này sẽ không cung cấp cho bạn một lỗi thời gian biên dịch. Thay vào đó, chương trình sẽ đột nhiên hoảng sợ hoặc làm điều gì đó kỳ lạ và không xác định bởi vì nó có trường sai, có nghĩa là ngay cả khi bạn là nhà phát triển gói đã thay đổi tên, bạn vẫn có thể không nhớ ở mọi nơi bạn đã thực hiện thủ thuật này và dành thời gian theo dõi lý do tại sao các thử nghiệm của bạn đột nhiên bị phá vỡ vì trình biên dịch không phàn nàn.Tôi đã đề cập rằng đây là một ý tưởng tồi?
Edit2: Kể từ khi bạn đề cập đến thử nghiệm White Box, lưu ý rằng nếu bạn đặt tên cho một tập tin trong thư mục của bạn <whatever>_test.go
nó sẽ không biên dịch, trừ khi bạn sử dụng go test
, vì vậy nếu bạn muốn làm thử nghiệm hộp màu trắng, ở đầu tuyên bố package <yourpackage>
mà sẽ cung cấp cho bạn quyền truy cập vào các trường chưa được xuất bản và nếu bạn muốn thực hiện thử nghiệm hộp đen thì bạn sử dụng package <yourpackage>_test
.
Nếu bạn cần hộp trắng kiểm tra hai gói cùng một lúc, tuy nhiên, tôi nghĩ bạn có thể bị kẹt và có thể cần phải suy nghĩ lại về thiết kế của bạn.
Bạn không thể nĩa thư viện hiện tại và hiển thị các trường bạn cần sửa đổi? (lưu ý rằng bạn nên cho rằng chúng không được phơi sáng vì một lý do chính đáng) – elithrar
@elithrar Tất cả đều là mã của tôi. Vì vậy ... có, chúng không được phơi sáng vì một lý do chính đáng; và vâng, tôi cần truy cập chúng. – Matt