shad-go/lectures/09-reflect/lecture.slide
2022-04-21 01:22:13 +03:00

276 lines
8.5 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

reflect
Лекция 9
Короткий Фёдор
* Reflection
- Механизм для чтения и изменения значений, не зная их реальный тип момент компиляции.
- Пример использования `reflect`: `fmt`, `encoding/json`.
- Эти пакеты используют `reflect` в реализации, но *не*светят* reflect в интерфейсе.
* Why Reflection?
Иногда нам нужно API, которое работает со значениями, которые не
объединены общим интерфейсом.
func Sprint(x interface{}) string {
type stringer interface {
String() string
}
switch x := x.(type) {
case stringer:
return x.String()
case string:
return x
case int:
return strconv.Itoa(x)
// ...similar cases for int16, uint32, and so on...
default:
// array, chan, func, map, pointer, slice, struct
return "???"
}
}
* reflect.Type and reflect.Value
- Пакет `reflect` определяет два основных типа: `reflect.Type` и `reflect.Value`.
- Единственная реализация `reflect.Type` - _type_descriptor_.
func TypeOf(interface{}) Type
- Пример:
t := reflect.TypeOf(3) // a reflect.Type
fmt.Println(t.String()) // "int"
fmt.Println(t) // "int"
- `reflect.TypeOf` всегда возвращает конкретный тип.
var w io.Writer = os.Stdout
fmt.Println(reflect.TypeOf(w)) // "*os.File"
- `Sprintf` формат `%T` использует `TypeOf` внутри.
fmt.Printf("%T\n", 3) // "int"
* reflect.Type and reflect.Value
- `reflect.Value` хранит значение любого типа.
v := reflect.ValueOf(3)
fmt.Println(v) // "3"
fmt.Printf("%v\n", v) // "3"
fmt.Println(v.String()) // NOTE: "<int Value>"
- Метод `.Type()` возвращает тип значения, хранящегося внутри `Value`.
t := v.Type() // a reflect.Type
fmt.Println(t.String()) // "int"
- Метод `.Interface()` - обратная операция к `reflect.ValueOf`.
v := reflect.ValueOf(3) // a reflect.Value
x := v.Interface() // an interface{}
i := x.(int) // an int
fmt.Printf("%d\n", i) // "3"
* Разница между reflect.Value и interface{}
- Оба хранят любое значение.
- `interface{}` - хранит значение, но не даёт доступа к нему. Можно достать конкретный тип, через _type_assertion_.
- `reflect.Value` - предоставляет доступ к значению внутри, вне зависимости от типа этого значения.
* format.Any
.code format/format.go /func Any/,/OMIT/
* format.Any
var x int64 = 1
var d time.Duration = 1 * time.Nanosecond
fmt.Println(format.Any(x)) // "1"
fmt.Println(format.Any(d)) // "1"
fmt.Println(format.Any([]int64{x})) // "[]int64 0x8202b87b0"
fmt.Println(format.Any([]time.Duration{d})) // "[]time.Duration 0x8202b87e0"
* Debug Display
type Movie struct {
Title, Subtitle string
Year int
Color bool
Actor map[string]string
Oscars []string
Sequel *string
}
strangelove := Movie{
Title: "Dr. Strangelove",
Subtitle: "How I Learned to Stop Worrying and Love the Bomb",
Year: 1964,
Color: false,
Actor: map[string]string{
"Dr. Strangelove": "Peter Sellers",
"Grp. Capt. Lionel Mandrake": "Peter Sellers",
"Pres. Merkin Muffley": "Peter Sellers",
"Gen. Buck Turgidson": "George C. Scott",
// ...
},
// ...
}
* Debug Display
Display("strangelove", strangelove)
Вывод:
Display strangelove (display.Movie):
strangelove.Title = "Dr. Strangelove"
strangelove.Subtitle = "How I Learned to Stop Worrying and Love the Bomb"
strangelove.Year = 1964
strangelove.Color = false
strangelove.Actor["Gen. Buck Turgidson"] = "George C. Scott"
strangelove.Actor["Brig. Gen. Jack D. Ripper"] = "Sterling Hayden"
strangelove.Actor["Maj. T.J. \"King\" Kong"] = "Slim Pickens"
strangelove.Actor["Dr. Strangelove"] = "Peter Sellers"
strangelove.Actor["Grp. Capt. Lionel Mandrake"] = "Peter Sellers"
strangelove.Actor["Pres. Merkin Muffley"] = "Peter Sellers"
strangelove.Oscars[0] = "Best Actor (Nomin.)"
strangelove.Oscars[1] = "Best Adapted Screenplay (Nomin.)"
strangelove.Oscars[2] = "Best Director (Nomin.)"
strangelove.Oscars[3] = "Best Picture (Nomin.)"
strangelove.Sequel = nil
* Debug Display
Display("os.Stderr", os.Stderr)
// Output:
// Display os.Stderr (*os.File):
// (*(*os.Stderr).file).fd = 2
// (*(*os.Stderr).file).name = "/dev/stderr"
// (*(*os.Stderr).file).nepipe = 0
* Debug Display
.code display/display.go /func Display/,/OMIT/
* Debug Display
.code display/display.go /func display/,/OMIT/
* Debug Display
.code display/display.go /case reflect.Ptr/,/OMIT/
* Setting Variables
- Некоторые выражения являются переменными: `x`, `x.f[1]`, `*p`.
- Некоторые выражения не являются переменными: `x`+`1`, `f(2)`.
- Переменные имеют _адрес_. Значение переменной можно изменить.
x := 2 // value type variable?
a := reflect.ValueOf(2) // 2 int no
b := reflect.ValueOf(x) // 2 int no
c := reflect.ValueOf(&x) // &x *int no
d := c.Elem() // 2 int yes
- `CanAddr`
fmt.Println(a.CanAddr()) // "false"
fmt.Println(b.CanAddr()) // "false"
fmt.Println(c.CanAddr()) // "false"
fmt.Println(d.CanAddr()) // "true"
* Setting Variables
x := 2
d := reflect.ValueOf(&x).Elem() // d refers to the variable x
px := d.Addr().Interface().(*int) // px := &x
*px = 3 // x = 3
fmt.Println(x) // "3"
- Или напрямую
d.Set(reflect.ValueOf(4))
fmt.Println(x) // "4"
- Или через специальный метод
d := reflect.ValueOf(&x).Elem()
d.SetInt(3)
fmt.Println(x) // "3"
- Нельзя менять _не_адресуемое_ значение.
x := 2
b := reflect.ValueOf(x)
b.Set(reflect.ValueOf(3)) // panic: Set using unaddressable value
* Setting Variables
Приватные поля можно читать, но нельзя менять.
stdout := reflect.ValueOf(os.Stdout).Elem() // *os.Stdout, an os.File var
fmt.Println(stdout.Type()) // "os.File"
fd := stdout.FieldByName("fd")
fmt.Println(fd.Int()) // "1"
fd.SetInt(2) // panic: unexported field
fmt.Println(fd.CanAddr(), fd.CanSet()) // "true false"
* Struct Tags
.code search/main.go /func search/,/^}/
* Struct Tags
.code params/params.go /func Unpack/,/OMIT/
.code params/params.go /Build map of/,/OMIT/
* Struct Tags
.code params/params.go /Update/,/^}/
* Struct Tags
.code params/params.go /func populate/,/^}/
* Method Set
.code methods/methods.go /func Print/,/^}/
methods.Print(time.Hour)
// Output:
// type time.Duration
// func (time.Duration) Hours() float64
// func (time.Duration) Minutes() float64
// func (time.Duration) Nanoseconds() int64
// func (time.Duration) Seconds() float64
// func (time.Duration) String() string
* Interface
- Результат `ValueOf` - всегда конкретный тип.
var w io.Writer
t := reflect.TypeOf(w) // t == ???
var v interface{} = w // v == nil
t = reflect.TypeOf(v) // t == ???
- Чтобы получить `reflect.Type` равный интерфейсу, нужно использовать промежуточный указатель.
var w io.Writer
ptrT := reflect.TypeOf(&w) // ptrT == *io.Writer
t := ptrT.Elem() // t == io.Writer
* Заключение
- `reflect` создаёт хрупкий код. Там, где была бы ошибка компилятора, возникает `panic` во время исполнения.
- Поведение `reflect` кода нужно документировать отдельно, и оно не ясно из типов аргументов.
- `reflect` работает медленнее, чем специализация под конкретный тип.