Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

أساسيات الـ Reflection

جزء من قسم البرمجة كائنية التوجه في رحلة GO على Coddy. الدرس 83 من 107.

الانعكاس يتيح للبرنامج فحص بنيته والتعامل معها أثناء وقت التشغيل. توفّر حزمة Go المسماة reflect هذه الإمكانية، مما يمكّنك من فحص الأنواع، وقراءة fields الخاصة بـ struct، واستدعاء الأساليب ديناميكيًا.

النوعان الأساسيان في reflection هما 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

تُعدّ reflection قوية، لكنها تأتي مع مقايضات: فهي تتجاوز التحقّق من الأنواع في وقت الترجمة، وتعمل ببطء أكبر من الوصول المباشر، وتجعل فهم الشيفرة أكثر صعوبة. استخدمها باعتدال في مهام مثل تسلسل JSON، أو تعيين ORM، أو بناء أدوات مساعدة عامة عندما لا يكون النوع معروفًا في وقت الترجمة.

challenge icon

التحدي

سهل

لنبنِ فاحصًا للبُنى يستخدم الانعكاس لفحص أي بنية والإبلاغ عن بنيتها! هذه حالة استخدام عملية للانعكاس: إنشاء أدوات تعمل مع أنواع غير معروفة وقت الترجمة.

ستنظّم كودك عبر ملفين:

  • inspector.go: أنشئ أداة الفحص المعتمدة على الانعكاس.

    أنشئ دالة InspectStruct(v any) string تستخدم الحزمة reflect لفحص أي بنية تُمرَّر إليها. يجب أن تُرجع دالتك تقريرًا منسقًا يتضمن:

    • اسم نوع البنية
    • نوع البنية (يجب أن يكون "struct")
    • عدد الحقول
    • لكل حقل: اسم الحقل ونوعه وقيمته الحالية

    يجب أن يكون تنسيق الإخراج كما يلي:

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

    استخدم reflect.TypeOf() للحصول على معلومات النوع، وreflect.ValueOf() للوصول إلى قيم الحقول. كرّر التكرار عبر الحقول باستخدام NumField() وField(i) على النوع للحصول على البيانات الوصفية، وField(i) على القيمة للحصول على القيم الفعلية.

  • main.go: عرّف بُنى نموذجية واستخدم الفاحص.

    اقرأ نوع بنية (product أو employee) وقيم حقولها، ثم أنشئ البنية المناسبة وافحصها.

    بالنسبة إلى product: أنشئ بنية Product تحتوي على حقول Name (من النوع string)، وPrice (من النوع float64)، وInStock (من النوع bool). اقرأ الاسم والسعر وحالة التوفر.

    بالنسبة إلى employee: أنشئ بنية Employee تحتوي على حقول ID (من النوع int)، وName (من النوع string)، وDepartment (من النوع string). اقرأ المعرّف والاسم والقسم.

    مرّر البنية التي أنشأتها إلى InspectStruct واطبع النتيجة.

سيتم توفير المدخلات التالية:

  • السطر 1: نوع البنية (product أو employee)
  • الأسطر التالية: قيم حقول ذلك النوع من البنية

على سبيل المثال، عند إدخال:

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

لاحظ كيف يعمل الفاحص مع أنواع مختلفة من البُنى من دون معرفة بنيتها مسبقًا. هذه هي قوة الانعكاس! تتعامل الدالة نفسها InspectStruct مع بُنى Product و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 عبر الإنترنت