การค้นหาเมธอดส่วนขยาย
ทำความเข้าใจว่าคอมไพเลอร์ค้นหาส่วนขยายอย่างไร
การค้นหาเมธอดส่วนขยาย เป็นบทเรียน C# Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน C# Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน
คอมไพเลอร์ค้นหาส่วนขยายอย่างไร
เมื่อคุณเขียน value.Method() แล้วไม่มีเมธอดของอินสแตนซ์ที่ตรงกัน คอมไพเลอร์จะค้นหาเมธอดส่วนขยาย แต่จะพิจารณาเฉพาะส่วนขยายที่มีเนมสเปซเจ้าของอยู่ในขอบเขตผ่านคำสั่ง using เท่านั้น
คำสั่งนำเข้าเนมสเปซทำให้ส่วนขยายอยู่ในขอบเขต
ส่วนขยายจะมองเห็นได้ก็ต่อเมื่อคุณนำเข้าเนมสเปซของส่วนขยายนั้น หากไม่มีคำสั่ง using ที่ถูกต้อง เมธอดจะดูเหมือนไม่มีอยู่ แม้ว่าจะถูกคอมไพล์ไว้ในแอสเซมบลีก็ตาม
using System;
using MyHelpers; // brings StringExtensions into scope
namespace MyHelpers
{
public static class StringExtensions
{
public static string Reverse(this string s)
{
var arr = s.ToCharArray();
Array.Reverse(arr);
return new string(arr);
}
}
}
public class Program
{
public static void Main()
{
Console.WriteLine("hello".Reverse());
}
}ตรวจสอบเมธอดของอินสแตนซ์ก่อน
คอมไพเลอร์แก้ไขการเรียกใช้เป็นลำดับขั้น โดยจะค้นหาเมธอดของ อินสแตนซ์ ที่ตรงกันก่อน รวมถึงเมธอดที่สืบทอดมา หากไม่มีเมธอดใดตรงกันจึงจะค้นหาเมธอดส่วนขยาย
using System;
using System.Collections.Generic;
public static class ListExt
{
// Ignored: List<int> already has Add(int)
public static void Add(this List<int> list, int a, int b)
=> list.Add(a + b);
}
public class Program
{
public static void Main()
{
var list = new List<int>();
list.Add(5); // the built-in instance Add wins for this signature
Console.WriteLine(list[0]);
}
}เลือกเนมสเปซที่อยู่ใกล้กว่า
หากมีส่วนขยายสองรายการที่ตรงกัน ระบบจะเลือกส่วนขยายที่มีเนมสเปซอยู่ใกล้กว่าในลำดับชั้นการซ้อน หากเกิดความกำกวมที่คอมไพเลอร์ไม่สามารถตัดสินได้ จะเกิดข้อผิดพลาดขณะคอมไพล์ ทำให้คุณต้องระบุให้ชัดเจน
using System;
namespace Outer
{
public static class A { public static string Tag(this int n) => "A" + n; }
namespace Inner
{
public static class B { public static string Tag(this int n) => "B" + n; }
public class Program
{
public static void Main()
{
// B is in the nearer (Inner) namespace, so it is preferred
Console.WriteLine(7.Tag());
}
}
}
}ระบุให้ชัดเจนด้วยการเรียกแบบสแตติก
เมื่อส่วนขัดแย้งกันหรือคุณต้องการระบุให้ชัดเจน ให้เรียกเมธอดแบบสแตติกโดยตรงด้วยชื่อคลาสแบบเต็ม วิธีนี้จะข้ามกระบวนการค้นหาเมธอดส่วนขยายทั้งหมด
using System;
public static class FormatExt
{
public static string Bracket(this string s) => "[" + s + "]";
}
public class Program
{
public static void Main()
{
// Explicit static call, no ambiguity possible
Console.WriteLine(FormatExt.Bracket("x"));
}
}การเลือกเมธอดหลายรูปแบบระหว่างส่วนขยาย
หากเมธอดส่วนขยายหลายเมธอดใช้ชื่อเดียวกัน ระบบการเลือกเมธอดตามชนิดของพารามิเตอร์จะเลือกเมธอดที่ตรงที่สุด เช่นเดียวกับการกำหนดเมธอดหลายรูปแบบทั่วไป
using System;
public static class PrintExt
{
public static string Describe(this int n) => "int:" + n;
public static string Describe(this string s) => "str:" + s;
}
public class Program
{
public static void Main()
{
Console.WriteLine(42.Describe());
Console.WriteLine("hi".Describe());
}
}การอนุมานชนิดข้อมูลระหว่างการค้นหา
สำหรับส่วนขยายชนิดข้อมูลทั่วไป คอมไพเลอร์จะอนุมานอาร์กิวเมนต์ชนิดข้อมูลจากตัวรับและอาร์กิวเมนต์ หากอนุมานไม่ได้ ระบบจะไม่พิจารณาส่วนขยายนั้น
using System;
using System.Collections.Generic;
public static class SeqExt
{
public static T FirstItem<T>(this IEnumerable<T> source)
{
foreach (var item in source) return item;
throw new InvalidOperationException("empty");
}
}
public class Program
{
public static void Main()
{
var nums = new List<int> { 5, 6, 7 };
// T inferred as int from the List<int> receiver
Console.WriteLine(nums.FirstItem());
}
}คำสั่ง using ทั่วทั้งโครงการและการนำเข้าโดยนัย
C# สมัยใหม่สามารถประกาศคำสั่ง global using เพื่อให้เนมสเปซอยู่ในขอบเขตทั่วทั้งโครงการ วิธีนี้ทำให้ไลบรารีส่วนขยายที่ใช้ร่วมกันพร้อมใช้งานได้ทุกที่โดยไม่ต้องเขียนคำสั่งนำเข้าซ้ำ
using System;
// In a real project a separate file might say: global using MyHelpers;
namespace MyHelpers
{
public static class IntExt { public static int Twice(this int n) => n * 2; }
}
public class Program
{
public static void Main()
{
// Works here because MyHelpers is referenced explicitly
Console.WriteLine(MyHelpers.IntExt.Twice(8));
}
}เมื่อชนิดของตัวรับมีความสำคัญ
การค้นหาขึ้นอยู่กับชนิดแบบสแตติกของตัวรับ ไม่ใช่ชนิดขณะทำงาน ส่วนขยายที่ประกาศไว้สำหรับชนิดพื้นฐานจะใช้งานได้แม้ตัวแปรจะเก็บอินสแตนซ์ของชนิดที่สืบทอดมา เว้นแต่จะมีส่วนขยายที่เฉพาะเจาะจงกว่าอยู่ในขอบเขต
using System;
public class Animal { }
public class Cat : Animal { }
public static class AnimalExt
{
public static string Kind(this Animal a) => "animal";
}
public class Program
{
public static void Main()
{
Animal a = new Cat();
// Chosen by static type Animal
Console.WriteLine(a.Kind());
}
}แบบจำลองทางความคิดสำหรับการค้นหา
ลองนึกถึงลำดับดังนี้: (1) เมธอดของอินสแตนซ์ จากนั้น (2) เมธอดส่วนขยายจากเนมสเปซที่นำเข้า โดยเริ่มจากเนมสเปซที่อยู่ใกล้ที่สุด และสุดท้าย (3) การเลือกเมธอดจากตัวเลือกที่มี การทราบลำดับนี้ช่วยอธิบายผลลัพธ์ที่ดูน่าประหลาดใจได้เกือบทั้งหมด
using System;
public static class Ext
{
public static string Wrap(this string s) => "<" + s + ">";
}
public class Program
{
public static void Main()
{
// No instance Wrap exists, so the extension is used
Console.WriteLine("node".Wrap());
}
}ลองทำด้วยตัวเอง
กำหนดส่วนขยายในเนมสเปซหนึ่ง นำเข้าเนมสเปซนั้น แล้วเรียกใช้ส่วนขยาย จากนั้นเรียกเมธอดเดียวกันอย่างชัดเจนในฐานะเมธอดแบบสแตติก เพื่อดูว่าทั้งสองวิธีทำงานได้
using System;
using Text.Helpers;
namespace Text.Helpers
{
public static class StringExtensions
{
public static int Vowels(this string s)
{
int count = 0;
foreach (var c in s.ToLower())
if ("aeiou".IndexOf(c) >= 0) count++;
return count;
}
}
}
public class Program
{
public static void Main()
{
Console.WriteLine("education".Vowels());
Console.WriteLine(Text.Helpers.StringExtensions.Vowels("education"));
}
}ตรวจสอบอย่างรวดเร็ว
ลองพิจารณาว่าอะไรทำให้เมธอดส่วนขยายมองเห็นได้
สรุปทบทวน
การค้นหาเมธอดส่วนขยายเป็นไปตามลำดับที่ชัดเจน
- ระบบจะลองใช้เมธอดของอินสแตนซ์ก่อน ส่วนขยายจะเข้ามาเติมเฉพาะกรณีที่ยังไม่มีเมธอดตรงกัน
- ส่วนขยายจะมองเห็นได้ก็ต่อเมื่อเนมสเปซของส่วนขยายอยู่ในขอบเขตผ่าน
usingหรือglobal using - เนมสเปซที่อยู่ใกล้กว่าจะได้รับเลือก ส่วนกรณีที่เสมอกันและแก้ความกำกวมไม่ได้จะเป็นข้อผิดพลาดขณะคอมไพล์
- คุณสามารถเรียกเมธอดแบบสแตติกอย่างชัดเจนได้เสมอเพื่อแก้ความกำกวม
- การค้นหาใช้ชนิดแบบสแตติกของตัวรับ
คำถามที่พบบ่อย
บทเรียน “การค้นหาเมธอดส่วนขยาย” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การค้นหาเมธอดส่วนขยาย” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส C# Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส C# Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การค้นหาเมธอดส่วนขยาย”
ทำความเข้าใจว่าคอมไพเลอร์ค้นหาส่วนขยายอย่างไร คุณปฏิบัติ C# Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน C# Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน C# Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน
บทเรียน “การค้นหาเมธอดส่วนขยาย” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน C# Academy นี้ได้ไหม
ได้ บทเรียน C# Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การกำหนดเมธอดส่วนขยาย
- การขยายอินเทอร์เฟซและชนิดทั่วไป
- การค้นหาเมธอดส่วนขยาย
- การออกแบบส่วนขยายที่ดี