0Pricing
C# Academy · บทเรียน

การออกแบบส่วนขยายที่ดี

ใช้แนวทางปฏิบัติที่ดีและหลีกเลี่ยงข้อผิดพลาด

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

ออกแบบส่วนขยายที่ผู้ใช้ชื่นชอบ

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

ขยายพฤติกรรม ไม่ใช่เอกลักษณ์

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

using System;

public static class TimeExtensions
{
    // A pleasant, focused helper
    public static bool IsWeekend(this DayOfWeek day)
        => day == DayOfWeek.Saturday || day == DayOfWeek.Sunday;
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine(DayOfWeek.Sunday.IsWeekend());
        Console.WriteLine(DayOfWeek.Monday.IsWeekend());
    }
}

ทำให้เมธอดไม่มีผลข้างเคียงเมื่อทำได้

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

using System;

public static class StringExtensions
{
    // Pure: depends only on input, returns a new string
    public static string Truncate(this string s, int max)
        => s.Length <= max ? s : s.Substring(0, max) + "...";
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine("Extension methods are great".Truncate(10));
    }
}

ป้องกันอินพุตที่ไม่มีค่า

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

using System;

public static class StringExtensions
{
    public static string SafeUpper(this string s)
    {
        if (s is null) throw new ArgumentNullException(nameof(s));
        return s.ToUpper();
    }
}

public class Program
{
    public static void Main()
    {
        try { ((string)null).SafeUpper(); }
        catch (ArgumentNullException ex) { Console.WriteLine("Caught: " + ex.ParamName); }
        Console.WriteLine("ok".SafeUpper());
    }
}

ตั้งชื่อให้ค้นหาได้ง่าย

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

using System;
using System.Collections.Generic;

public static class CollectionExtensions
{
    public static bool HasItems<T>(this ICollection<T> c)
        => c != null && c.Count > 0;
}

public class Program
{
    public static void Main()
    {
        var list = new List<int> { 1 };
        Console.WriteLine(list.HasItems());
    }
}

จัดกลุ่มส่วนขยายที่เกี่ยวข้องให้เป็นหมวดหมู่

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

using System;
using System.Collections.Generic;

public static class EnumerableExtensions
{
    public static IEnumerable<T> TakeFirst<T>(this IEnumerable<T> source, int n)
    {
        int taken = 0;
        foreach (var item in source)
        {
            if (taken++ >= n) yield break;
            yield return item;
        }
    }
}

public class Program
{
    public static void Main()
    {
        var nums = new List<int> { 1, 2, 3, 4 };
        Console.WriteLine(string.Join(",", nums.TakeFirst(2)));
    }
}

อย่าซ่อนพฤติกรรมที่น่าประหลาดใจ

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

using System;

public static class MathExtensions
{
    // Cheap and obvious: no hidden cost
    public static int ClampMin(this int value, int min)
        => value < min ? min : value;
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine((-3).ClampMin(0));
        Console.WriteLine(5.ClampMin(0));
    }
}

หลีกเลี่ยงการขยาย object

การขยาย object จะทำให้ทุกชนิดในโปรแกรมมีเมธอดของคุณปะปนอยู่ และทำให้ IntelliSense รกไปทุกแห่ง ให้กำหนดเป้าหมายเป็นชนิดข้อมูลหรืออินเทอร์เฟซที่เฉพาะเจาะจงที่สุดเท่าที่เหมาะสมแทน

using System;

public static class IntExtensions
{
    // Specific to int, not object - far less noisy
    public static string ToOrdinal(this int n)
    {
        if (n % 10 == 1 && n % 100 != 11) return n + "st";
        if (n % 10 == 2 && n % 100 != 12) return n + "nd";
        if (n % 10 == 3 && n % 100 != 13) return n + "rd";
        return n + "th";
    }
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine(1.ToOrdinal());
        Console.WriteLine(22.ToOrdinal());
    }
}

ควรส่งคืน IEnumerable สำหรับลำดับ

สำหรับตัวดำเนินการของลำดับ ให้ส่งคืน IEnumerable<T> และใช้การประมวลผลแบบเลื่อนเวลา วิธีนี้ประกอบเข้าด้วยกันได้ดี และไม่บังคับให้ผู้เรียกสร้างข้อมูลเป็นคอลเลกชันทั้งที่อาจไม่จำเป็นต้องใช้

using System;
using System.Collections.Generic;

public static class SeqExtensions
{
    public static IEnumerable<int> Doubled(this IEnumerable<int> source)
    {
        foreach (var n in source) yield return n * 2;
    }
}

public class Program
{
    public static void Main()
    {
        var nums = new List<int> { 1, 2, 3 };
        foreach (var n in nums.Doubled()) Console.WriteLine(n);
    }
}

รายการตรวจสอบสำหรับส่วนขยายที่ดี

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

using System;

public static class StringExtensions
{
    public static string OrDefault(this string s, string fallback)
        => string.IsNullOrEmpty(s) ? fallback : s;
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine("".OrDefault("N/A"));
        Console.WriteLine("value".OrDefault("N/A"));
    }
}

ลองทำด้วยตัวเอง

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

using System;
using System.Collections.Generic;

public static class DictionaryExtensions
{
    // Clear name, specific type, null-safe, pure
    public static TValue GetOrDefault<TKey, TValue>(
        this IReadOnlyDictionary<TKey, TValue> dict, TKey key, TValue fallback)
    {
        if (dict == null) return fallback;
        return dict.TryGetValue(key, out var value) ? value : fallback;
    }
}

public class Program
{
    public static void Main()
    {
        var ages = new Dictionary<string, int> { ["Ann"] = 30 };
        Console.WriteLine(ages.GetOrDefault("Ann", -1));
        Console.WriteLine(ages.GetOrDefault("Bob", -1));
    }
}

ตรวจสอบอย่างรวดเร็ว

นำแนวทางการออกแบบไปใช้

สรุปทบทวน

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

  • ควรใช้ตัวช่วยที่ไม่มีผลข้างเคียง ส่งคืนค่า และเชื่อมต่อได้ดี
  • ตัดสินใจและบันทึกพฤติกรรมเมื่อได้รับค่าที่ไม่มีค่า
  • ใช้ชื่อที่ชัดเจนและเป็นคำกริยาเพื่อให้ค้นหาได้ง่าย
  • กำหนดเป้าหมายเป็นชนิดข้อมูลหรืออินเทอร์เฟซที่เฉพาะเจาะจง ไม่ใช่ object
  • จัดกลุ่มตัวช่วยที่เกี่ยวข้อง และส่งคืน IEnumerable<T> สำหรับลำดับ

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

บทเรียน “การออกแบบส่วนขยายที่ดี” ฟรีหรือไม่

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

คุณจะเรียนรู้อะไรในบทเรียน “การออกแบบส่วนขยายที่ดี”

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

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

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

บทเรียน “การออกแบบส่วนขยายที่ดี” ใช้เวลานานแค่ไหน

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

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

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

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

  1. การกำหนดเมธอดส่วนขยาย
  2. การขยายอินเทอร์เฟซและชนิดทั่วไป
  3. การค้นหาเมธอดส่วนขยาย
  4. การออกแบบส่วนขยายที่ดี
← กลับไปที่ C# Academy