การออกแบบส่วนขยายที่ดี
ใช้แนวทางปฏิบัติที่ดีและหลีกเลี่ยงข้อผิดพลาด
การออกแบบส่วนขยายที่ดี เป็นบทเรียน 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การกำหนดเมธอดส่วนขยาย
- การขยายอินเทอร์เฟซและชนิดทั่วไป
- การค้นหาเมธอดส่วนขยาย
- การออกแบบส่วนขยายที่ดี