Swift Academy · บทเรียน

@dynamicMemberLookup

สร้างการเข้าถึงสมาชิกแบบไดนามิกที่ปลอดภัยด้านชนิด

บทเรียน 4 จาก 413 ขั้นตอน

@dynamicMemberLookup เป็นบทเรียน Swift Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Swift Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Swift Academy มีบทเรียนทั้งหมด 4 บทเรียน

การค้นหาสมาชิกแบบไดนามิกคืออะไร

แอตทริบิวต์ @dynamicMemberLookup ช่วยให้ชนิดข้อมูลตอบสนองต่อการเข้าถึงสมาชิกด้วยรูปแบบจุด แม้สมาชิกนั้นจะไม่ได้ประกาศเป็นคุณสมบัติจริง เมื่อคุณเขียน value.something Swift จะส่งต่อการเรียกไปยังซับสคริปต์พิเศษที่คุณกำหนด

วิธีนี้ทำให้ชนิดตัวห่อหุ้มใช้งานได้เป็นธรรมชาติ ขณะเดียวกันก็รวมจุดที่จัดการการเข้าถึงไว้ในที่เดียว

@dynamicMemberLookup
struct Settings {
    private var storage: [String: String] = ["theme": "dark"]
    subscript(dynamicMember key: String) -> String? {
        storage[key]
    }
}

let s = Settings()
print(s.theme ?? "none")

ซับสคริปต์ที่จำเป็น

ชนิดข้อมูลที่ทำเครื่องหมายด้วย @dynamicMemberLookup ต้องใช้งาน subscript(dynamicMember:) เมื่อพารามิเตอร์เป็น String ชื่อสมาชิกที่คุณพิมพ์จะกลายเป็นสตริงนั้นขณะทำงาน

ดังนั้น config.timeout จะเรียกซับสคริปต์ด้วยคีย์ "timeout"

@dynamicMemberLookup
struct Config {
    let values: [String: Int]
    subscript(dynamicMember member: String) -> Int {
        values[member] ?? 0
    }
}

let config = Config(values: ["timeout": 30, "retries": 3])
print(config.timeout)
print(config.retries)

การค้นหาด้วยสตริงไม่ปลอดภัยด้านชนิดข้อมูล

การค้นหาสมาชิกแบบไดนามิกที่ใช้สตริงมีความยืดหยุ่น แต่สูญเสียการตรวจสอบขณะคอมไพล์ หากคุณสะกดชื่อสมาชิกผิด โค้ดก็ยังคอมไพล์ได้ และคุณจะได้รับสิ่งที่ซับสคริปต์คืนค่าสำหรับคีย์ที่ไม่มีอยู่

ในตัวอย่างด้านล่าง สมาชิกที่ไม่รู้จักจะคืนค่าเริ่มต้นอย่างเงียบ ๆ แทนที่จะทำให้การสร้างโปรแกรมล้มเหลว

@dynamicMemberLookup
struct Config {
    let values: [String: Int]
    subscript(dynamicMember member: String) -> Int {
        values[member] ?? -1
    }
}

let config = Config(values: ["timeout": 30])
print(config.tmout)

การค้นหาที่ปลอดภัยด้านชนิดข้อมูลด้วยคีย์พาธ

เพื่อความปลอดภัย คุณสามารถให้ซับสคริปต์รับ KeyPath แทนสตริงได้ คราวนี้คอมไพเลอร์จะตรวจสอบว่าสมาชิกนั้นมีอยู่ในชนิดข้อมูลที่ถูกห่อหุ้มหรือไม่ ทำให้กลับมาตรวจสอบได้อย่างครบถ้วนขณะยังส่งต่อการเข้าถึงอยู่

รูปแบบนี้เหมาะอย่างยิ่งกับชนิดตัวห่อหุ้มที่เก็บค่าซึ่งมีชนิดข้อมูลชัดเจน

struct User { let name: String; let age: Int }

@dynamicMemberLookup
struct Boxed {
    let user: User
    subscript<T>(dynamicMember path: KeyPath<User, T>) -> T {
        user[keyPath: path]
    }
}

let boxed = Boxed(user: User(name: "Ada", age: 36))
print(boxed.name)
print(boxed.age)

เหตุใดรูปแบบคีย์พาธจึงปลอดภัยกว่า

เนื่องจากซับสคริปต์รับ KeyPath<User, T> จึงอนุญาตเฉพาะสมาชิกที่มีอยู่จริงใน User เท่านั้น การพิมพ์ผิดอย่าง boxed.naem จะทำให้คอมไพล์ไม่ผ่าน ต่างจากรูปแบบที่ใช้สตริง

คุณได้ความสะดวกของรูปแบบจุดโดยไม่มีความเสี่ยงดังกล่าว

struct User { let name: String; let age: Int }

@dynamicMemberLookup
struct ReadOnly {
    let value: User
    subscript<T>(dynamicMember path: KeyPath<User, T>) -> T {
        value[keyPath: path]
    }
}

let r = ReadOnly(value: User(name: "Lin", age: 22))
print(r.name, r.age)

สมาชิกไดนามิกแบบเขียนได้

หากซับสคริปต์มีตัวกำหนดค่าและรับ WritableKeyPath คุณก็สามารถกำหนดค่าให้กับสมาชิกที่ส่งต่อได้ ตัวห่อหุ้มจะส่งต่อการเขียนไปยังค่าที่ถูกห่อหุ้มโดยตรง

วิธีนี้ทำให้ได้ตัวห่อหุ้มที่เปลี่ยนแปลงได้และทำงานอย่างโปร่งใส

struct Profile { var name: String; var score: Int }

@dynamicMemberLookup
struct Wrapper {
    var value: Profile
    subscript<T>(dynamicMember path: WritableKeyPath<Profile, T>) -> T {
        get { value[keyPath: path] }
        set { value[keyPath: path] = newValue }
    }
}

var w = Wrapper(value: Profile(name: "Mo", score: 0))
w.score = 99
print(w.value.score)

การส่งต่อไปยังอ็อบเจ็กต์ที่ถูกห่อหุ้ม

การค้นหาสมาชิกแบบไดนามิกเหมาะอย่างยิ่งกับพร็อกซีขนาดเล็กที่ส่งต่อไปยังค่าภายในเป็นส่วนใหญ่ แต่เพิ่มพฤติกรรมบางอย่าง ตัวซับสคริปต์ช่วยรวมตรรกะการส่งต่อไว้ในที่เดียว

ที่นี่ตัวห่อหุ้มสำหรับการบันทึกข้อมูลจะส่งต่อการ read และแสดงสมาชิกที่ถูกเข้าถึง

struct Account { let id: Int; let balance: Double }

@dynamicMemberLookup
struct Logged {
    let account: Account
    subscript<T>(dynamicMember path: KeyPath<Account, T>) -> T {
        let result = account[keyPath: path]
        print("accessed a member")
        return result
    }
}

let logged = Logged(account: Account(id: 7, balance: 100))
print(logged.balance)

การทำงานร่วมกับแนวคิดการเรียกใช้งานแบบไดนามิก

การค้นหาสมาชิกแบบไดนามิกทำงานร่วมกับชนิดข้อมูลทั่วไปได้ดี ซับสคริปต์ทั่วไปเพียงรายการเดียวสามารถส่งต่อคุณสมบัติที่มีชนิดใดก็ได้ ดังนั้นตัวห่อหุ้มหนึ่งตัวจึงใช้กับค่าที่มีรูปแบบหลากหลายได้

ที่นี่ Proxy เดียวกันใช้ได้ไม่ว่าสมาชิกนั้นจะเป็นสตริงหรือจำนวนเต็ม

struct Item { let title: String; let count: Int }

@dynamicMemberLookup
struct Proxy<Wrapped> {
    let wrapped: Wrapped
    subscript<T>(dynamicMember path: KeyPath<Wrapped, T>) -> T {
        wrapped[keyPath: path]
    }
}

let proxy = Proxy(wrapped: Item(title: "Book", count: 4))
print(proxy.title, proxy.count)

การค้นหาด้วยสตริงสำหรับข้อมูลลักษณะ JSON

รูปแบบที่ใช้สตริงยังมีประโยชน์สำหรับการแทนข้อมูลที่มีโครงสร้างไม่ตายตัว เช่น JSON ที่แยกวิเคราะห์แล้ว ซึ่งไม่ทราบสมาชิกขณะคอมไพล์ การเข้าถึงแต่ละครั้งจะกลายเป็นการค้นหาในพจนานุกรม

ที่นี่ค่าขนาดเล็กที่มีลักษณะเหมือน JSON จะเปิดเผย fields ผ่านรูปแบบจุด

@dynamicMemberLookup
struct JSON {
    let fields: [String: String]
    subscript(dynamicMember key: String) -> String {
        fields[key] ?? ""
    }
}

let json = JSON(fields: ["city": "Oslo", "country": "Norway"])
print(json.city)
print(json.country)

ข้อจำกัดที่ควรคำนึงถึง

การค้นหาสมาชิกแบบไดนามิกมีผลเฉพาะไวยากรณ์การเข้าถึงสมาชิกเท่านั้น ไม่ได้เพิ่มคุณสมบัติที่จัดเก็บจริง เครื่องมืออย่างการเติมโค้ดอัตโนมัติทำงานกับรูปแบบคีย์พาธได้ แต่ไม่ทำงานกับคีย์สตริงที่กำหนดขึ้นเอง

ควรเลือกรูปแบบคีย์พาธเมื่อโครงสร้างพื้นฐานเป็นที่รู้จัก และสงวนรูปแบบสตริงไว้สำหรับข้อมูลที่เป็นไดนามิกอย่างแท้จริง

struct Point { let x: Int; let y: Int }

@dynamicMemberLookup
struct Safe {
    let point: Point
    subscript<T>(dynamicMember path: KeyPath<Point, T>) -> T {
        point[keyPath: path]
    }
}

let safe = Safe(point: Point(x: 3, y: 4))
print(safe.x + safe.y)

ตัวตกแต่งที่ใช้งานได้จริง

เมื่อนำแนวคิดทั้งหมดมารวมกัน ต่อไปนี้คือตัวห่อหุ้มที่เก็บสถิติ โดยส่งต่อการ read คุณสมบัติทุกครั้งไปยังค่าภายในพร้อมนับจำนวนการเข้าถึง กรอบงานจริงใช้แนวคิดนี้สำหรับการเฝ้าสังเกตและการติดตาม

รูปแบบจุดบนตัวห่อหุ้มให้ความรู้สึกเหมือนกับการใช้ value โดยตรงทุกประการ

struct Sensor { let temperature: Double; let humidity: Double }

@dynamicMemberLookup
class Tracked {
    let sensor: Sensor
    var reads = 0
    init(_ s: Sensor) { sensor = s }
    subscript<T>(dynamicMember path: KeyPath<Sensor, T>) -> T {
        reads += 1
        return sensor[keyPath: path]
    }
}

let t = Tracked(Sensor(temperature: 21.5, humidity: 0.4))
print(t.temperature)
print(t.humidity)
print(t.reads)

ตรวจสอบด่วน: การค้นหาแบบไดนามิกที่ปลอดภัยด้านชนิดข้อมูล

ลองนึกดูว่าพารามิเตอร์ของซับสคริปต์ใดทำให้การค้นหาสมาชิกแบบไดนามิกปลอดภัยด้านชนิดข้อมูล

สรุป: การค้นหาสมาชิกแบบไดนามิก

คุณได้เรียนรู้การทำงานของ @dynamicMemberLookup แล้ว:

  • แอตทริบิวต์นี้จะส่งต่อการเข้าถึงด้วยไวยากรณ์จุดที่ไม่ได้ประกาศไปยัง subscript(dynamicMember:)
  • พารามิเตอร์ String ช่วยให้เข้าถึงข้อมูลได้อย่างยืดหยุ่นแต่ไม่มีการตรวจสอบชนิด เหมาะสำหรับข้อมูลรูปแบบคล้ายเจสัน
  • พารามิเตอร์ KeyPath ช่วยให้กลับมามีความปลอดภัยด้านชนิดข้อมูลอย่างสมบูรณ์และใช้การเติมโค้ดอัตโนมัติได้
  • WritableKeyPath ที่มีตัวตั้งค่าช่วยให้คุณส่งต่อการเขียนผ่านตัวห่อหุ้มได้
  • นี่เป็นความสะดวกด้านไวยากรณ์ที่เข้าคู่กับรูปแบบพร็อกซีและเดคอเรเตอร์ได้อย่างเป็นธรรมชาติ

ตอนนี้คุณมีชุดเครื่องมือเส้นทางคีย์ครบถ้วนแล้ว ทั้งการอ่าน การเขียน การใช้งานกับคอลเลกชัน และการส่งต่อแบบไดนามิก

struct User { let name: String }

@dynamicMemberLookup
struct Wrap {
    let user: User
    subscript<T>(dynamicMember path: KeyPath<User, T>) -> T {
        user[keyPath: path]
    }
}

print(Wrap(user: User(name: "Final")).name)
เริ่มต้นได้ฟรี

เรียนรู้ Swift ด้วย AI tutor — ฟรี

เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป

คอร์ส
122
บทเรียน
409

คำถามที่พบบ่อย

บทเรียน “@dynamicMemberLookup” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “@dynamicMemberLookup” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Swift Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Swift Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “@dynamicMemberLookup”

สร้างการเข้าถึงสมาชิกแบบไดนามิกที่ปลอดภัยด้านชนิด คุณปฏิบัติ Swift Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Swift Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Swift Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน

บทเรียน “@dynamicMemberLookup” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Swift Academy นี้ได้ไหม

ได้ บทเรียน Swift Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. พื้นฐาน KeyPath
  2. การใช้ KeyPaths ในคอลเลกชัน
  3. KeyPath ที่เขียนได้และอ้างอิงได้
  4. @dynamicMemberLookup
← กลับไปที่ Swift Academy