Menu
Coddy logo textTech

יסודות Reflection

חלק מהיחידה תכנות מונחה עצמים במסלול ה-GO של Coddy. שיעור 83 מתוך 107.

התבוננות פנימית מאפשרת לתוכנית לבחון ולתפעל את המבנה של עצמה בזמן ריצה. החבילה reflect של Go מספקת את היכולת הזאת, ומאפשרת לך לבדוק טיפוסים, לקרוא שדות של מבנים ולקרוא למתודות באופן דינמי.

שני סוגי הליבה ברפלקציה הם reflect.Type ו־reflect.Value. מקבלים אותם באמצעות reflect.TypeOf() ו־reflect.ValueOf():

import "reflect"

type Person struct {
    Name string
    Age  int
}

p := Person{Name: "Alice", Age: 30}

t := reflect.TypeOf(p)
v := reflect.ValueOf(p)

fmt.Println(t.Name())       // Person
fmt.Println(t.Kind())       // struct
fmt.Println(v.NumField())   // 2

אפשר לעבור על השדות של המבנה כדי לבדוק את שמותיהם, טיפוסיהם וערכיהם:

for i := 0; i < t.NumField(); i++ {
    field := t.Field(i)
    value := v.Field(i)
    fmt.Printf("%s: %v\n", field.Name, value)
}
// פלט:
// Name: Alice
// Age: 30

רפלקציה היא עוצמתית, אך כרוכה בפשרות: היא עוקפת בדיקת טיפוסים בזמן הידור, פועלת לאט יותר מגישה ישירה ומקשה על הבנת הקוד. השתמשו בה במשורה למשימות כמו סריאליזציה ל-JSON, מיפוי ORM או בניית כלי עזר כלליים, שבהן הטיפוס אינו ידוע בזמן הידור.

challenge icon

אתגר

קל

בואו נבנה בודק struct שמשתמש ברפלקציה כדי לבחון כל struct ולדווח על המבנה שלו! זהו שימוש מעשי ברפלקציה: יצירת כלי עזר שעובדים עם טיפוסים שאינם ידועים בזמן הקומפילציה.

תארגנו את הקוד שלכם בשני קבצים:

  • inspector.go: צרו כלי עזר לבדיקה המבוסס על רפלקציה.

    בנו פונקציה InspectStruct(v any) string שמשתמשת בחבילה reflect כדי לבחון כל struct שמועבר אליה. הפונקציה שלכם צריכה להחזיר דוח מעוצב הכולל:

    • שם הטיפוס של ה-struct
    • ה-kind של ה-struct (אמור להיות "struct")
    • מספר השדות
    • עבור כל שדה: שם השדה, הטיפוס שלו והערך הנוכחי שלו

    פורמט הפלט צריך להיות:

    Type: [TypeName]
    Kind: struct
    Fields: [count]
    - [FieldName] ([FieldType]): [Value]
    - [FieldName] ([FieldType]): [Value]
    ...

    השתמשו ב-reflect.TypeOf() כדי לקבל מידע על הטיפוס וב-reflect.ValueOf() כדי לגשת לערכי השדות. עברו על השדות באמצעות NumField(), באמצעות Field(i) על הטיפוס כדי לקבל מטא-נתונים, ובאמצעות Field(i) על הערך כדי לקבל את הערכים בפועל.

  • main.go: הגדירו struct-ים לדוגמה והשתמשו בבודק שלכם.

    קראו טיפוס של struct (product או employee) ואת ערכי השדות שלו, ואז צרו את ה-struct המתאים ובדקו אותו.

    עבור product: צרו struct מסוג Product עם השדות Name (מחרוזת), Price (float64) ו-InStock (בוליאני). קראו את השם, המחיר ומצב המלאי.

    עבור employee: צרו struct מסוג Employee עם השדות ID (מספר שלם), Name (מחרוזת) ו-Department (מחרוזת). קראו את המזהה, השם והמחלקה.

    העבירו את ה-struct שיצרתם אל InspectStruct והדפיסו את התוצאה.

הקלטים הבאים יסופקו:

  • שורה 1: טיפוס ה-struct (product או employee)
  • השורות הבאות: ערכי השדות עבור טיפוס ה-struct הזה

לדוגמה, עבור הקלט:

product
Laptop
999.99
true

הפלט שלכם צריך להיות:

Type: Product
Kind: struct
Fields: 3
- Name (string): Laptop
- Price (float64): 999.99
- InStock (bool): true

ועבור הקלט:

employee
42
Alice Johnson
Engineering

הפלט שלכם צריך להיות:

Type: Employee
Kind: struct
Fields: 3
- ID (int): 42
- Name (string): Alice Johnson
- Department (string): Engineering

ועבור הקלט:

product
Headphones
79.50
false

הפלט שלכם צריך להיות:

Type: Product
Kind: struct
Fields: 3
- Name (string): Headphones
- Price (float64): 79.5
- InStock (bool): false

שימו לב שהבודק שלכם פועל עם טיפוסים שונים של struct בלי לדעת מראש מה המבנה שלהם. זהו כוחה של הרפלקציה! אותה פונקציה InspectStruct מטפלת באופן דינמי גם ב-struct מסוג Product וגם ב-struct מסוג Employee.

נסו בעצמכם

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

// מבנה Product עם השדות Name, Price ו-InStock
type Product struct {
	Name    string
	Price   float64
	InStock bool
}

// מבנה Employee עם השדות ID, Name ו-Department
type Employee struct {
	ID         int
	Name       string
	Department string
}

func main() {
	reader := bufio.NewReader(os.Stdin)

	// קרא את סוג המבנה
	structType, _ := reader.ReadString('\n')
	structType = strings.TrimSpace(structType)

	if structType == "product" {
		// קרא את שדות המוצר
		name, _ := reader.ReadString('\n')
		name = strings.TrimSpace(name)

		priceStr, _ := reader.ReadString('\n')
		priceStr = strings.TrimSpace(priceStr)
		price, _ := strconv.ParseFloat(priceStr, 64)

		inStockStr, _ := reader.ReadString('\n')
		inStockStr = strings.TrimSpace(inStockStr)
		inStock, _ := strconv.ParseBool(inStockStr)

		// TODO: צור מבנה Product עם הערכים שנקראו
		// TODO: קרא ל-InspectStruct עם המוצר והדפס את התוצאה

	} else if structType == "employee" {
		// קרא את שדות העובד
		idStr, _ := reader.ReadString('\n')
		idStr = strings.TrimSpace(idStr)
		id, _ := strconv.Atoi(idStr)

		name, _ := reader.ReadString('\n')
		name = strings.TrimSpace(name)

		department, _ := reader.ReadString('\n')
		department = strings.TrimSpace(department)

		// TODO: צור מבנה Employee עם הערכים שנקראו
		// TODO: קרא ל-InspectStruct עם העובד והדפס את התוצאה

	}
}
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר Go אונליין