ก่อนอื่นเรามาพูดเกี่ยวกับพาราเมทริกพหุสัณฐานที่บริสุทธิ์
ตัวแปรหลายตัวแปรหมายความว่าอย่างไร ก็หมายความว่าประเภทหรือตัวสร้างประเภทค่อนข้างแปรผันตามประเภท เนื่องจากชนิดถูกส่งเป็นพารามิเตอร์คุณไม่สามารถทราบล่วงหน้าได้ว่ามันอาจเป็นอะไร คุณไม่สามารถตั้งสมมติฐานใด ๆ ทีนี้ถ้าคุณไม่รู้ว่ามันคืออะไรแล้วการใช้งานคืออะไร? คุณทำอะไรกับมันได้บ้าง
ตัวอย่างเช่นคุณสามารถจัดเก็บและเรียกคืนได้ นั่นเป็นกรณีที่คุณพูดถึงแล้ว: คอลเล็กชัน ในการจัดเก็บรายการในรายการหรืออาร์เรย์ฉันไม่จำเป็นต้องรู้อะไรเกี่ยวกับสินค้า รายการหรืออาร์เรย์สามารถลบล้างชนิด
แต่สิ่งที่เกี่ยวกับMaybe
ประเภท? หากคุณไม่คุ้นเคยกับมันMaybe
เป็นประเภทที่อาจมีค่าและอาจไม่ คุณจะใช้มันที่ไหน ตัวอย่างเช่นเมื่อนำไอเท็มออกจากพจนานุกรมความจริงที่ว่าไอเท็มอาจไม่ได้อยู่ในพจนานุกรมนั้นไม่ใช่สถานการณ์ที่พิเศษดังนั้นคุณไม่ควรทิ้งข้อยกเว้นหากไม่มีไอเท็มนั้น แต่คุณกลับตัวอย่างของชนิดย่อยของหนึ่งMaybe<T>
ซึ่งมีตรงสองชนิดย่อย: และNone
เป็นผู้สมัครของสิ่งที่จริง ๆ ควรกลับแทนการทิ้งข้อยกเว้นหรือการเต้นรำทั้งหมดSome<T>
int.Parse
Maybe<int>
int.TryParse(out bla)
ตอนนี้คุณอาจโต้แย้งว่าMaybe
kinda-sorta เหมือนรายการที่สามารถมีศูนย์หรือองค์ประกอบเดียวเท่านั้น และมันก็เป็นคอลเล็กชั่น
ถ้าเช่นนั้นจะเป็นTask<T>
อย่างไร มันเป็นประเภทที่สัญญาว่าจะคืนค่าในบางจุดในอนาคต แต่ไม่จำเป็นต้องมีค่าในตอนนี้
หรือเกี่ยวกับFunc<T, …>
อะไร คุณจะแสดงแนวคิดของฟังก์ชันจากประเภทหนึ่งไปยังอีกประเภทหนึ่งได้อย่างไรถ้าคุณไม่สามารถสรุปนามธรรมได้
หรือมากกว่าโดยทั่วไป: การพิจารณาว่านามธรรมและการนำกลับมาใช้ใหม่เป็นงานพื้นฐานสองประการของวิศวกรรมซอฟต์แวร์ทำไมคุณไม่ต้องการให้นามธรรมเกินประเภท?
ตอนนี้เรามาพูดถึงพหุนามที่มีขอบเขต polymorphism bounded เป็นพื้นที่แตกต่างคณิตศาสตร์และชนิดย่อย polymorphism พบ: แทนที่จะเป็นตัวสร้างประเภทเป็นสมบูรณ์ลืมเกี่ยวกับพารามิเตอร์ชนิดของคุณสามารถผูก (หรืออุปสรรค) ประเภทที่จะเป็นชนิดย่อยของประเภทที่ระบุไว้บางส่วน
กลับไปที่คอลเล็กชัน ใช้ hashtable เรากล่าวไว้ข้างต้นว่ารายการไม่จำเป็นต้องรู้อะไรเกี่ยวกับองค์ประกอบ ดี hashtable ไม่: มันจำเป็นต้องรู้ว่ามันสามารถแฮชพวกเขา (หมายเหตุ: ใน C # วัตถุทั้งหมดสามารถแฮชได้เหมือนกับวัตถุทั้งหมดที่สามารถนำมาเปรียบเทียบเพื่อความเท่าเทียมกันได้ซึ่งไม่เป็นความจริงสำหรับทุกภาษาแม้ว่าบางครั้งจะถือว่าผิดพลาดในการออกแบบแม้ใน C #)
ดังนั้นคุณต้องการ จำกัด พารามิเตอร์ประเภทของคุณสำหรับประเภทคีย์ใน hashtable ให้เป็นตัวอย่างของIHashable
:
class HashTable<K, V> where K : IHashable
{
Maybe<V> Get(K key);
bool Add(K key, V value);
}
ลองนึกภาพถ้าคุณมีสิ่งนี้:
class HashTable
{
object Get(IHashable key);
bool Add(IHashable key, object value);
}
คุณจะทำอะไรกับvalue
คุณออกไปจากที่นั่น? คุณไม่สามารถทำอะไรกับมันได้คุณรู้แค่ว่ามันเป็นวัตถุ และถ้าคุณทำซ้ำมันสิ่งที่คุณได้คือคู่ของสิ่งที่คุณรู้คือIHashable
(ซึ่งไม่ได้ช่วยอะไรมากเพราะมันมีคุณสมบัติเดียวเท่านั้นHash
) และสิ่งที่คุณรู้คือobject
(ซึ่งช่วยคุณได้น้อยลง)
หรือบางสิ่งตามตัวอย่างของคุณ:
class Repository<T> where T : ISerializable
{
T Get(int id);
void Save(T obj);
void Delete(T obj);
}
รายการจะต้องต่อเนื่องได้เนื่องจากกำลังจะถูกเก็บไว้ในดิสก์ แต่ถ้าคุณมีสิ่งนี้แทน:
class Repository
{
ISerializable Get(int id);
void Save(ISerializable obj);
void Delete(ISerializable obj);
}
กับกรณีทั่วไปถ้าคุณใส่BankAccount
ในคุณจะได้รับBankAccount
กลับมาพร้อมกับวิธีการและคุณสมบัติชอบOwner
, AccountNumber
, Balance
, Deposit
, Withdraw
ฯลฯ สิ่งที่คุณสามารถทำงานร่วมกับ ตอนนี้กรณีอื่น ๆ ? คุณใส่ในBankAccount
แต่คุณได้รับกลับซึ่งมีเพียงหนึ่งในสถานที่ให้บริการ:Serializable
AsString
คุณจะทำอะไรกับสิ่งนั้น
นอกจากนี้ยังมีเทคนิคบางอย่างที่คุณสามารถทำได้ด้วย polymorphism ที่มีขอบเขต:
ปริมาณ F-bounded นั้นโดยทั่วไปแล้วตัวแปรประเภทจะปรากฏขึ้นอีกครั้งในข้อ จำกัด สิ่งนี้มีประโยชน์ในบางสถานการณ์ เช่นคุณจะเขียนICloneable
อินเตอร์เฟสได้อย่างไร? คุณจะเขียนวิธีที่ชนิดคืนค่าเป็นประเภทของคลาสที่ใช้งานได้อย่างไร ในภาษาที่มีคุณสมบัติMyTypeนั้นเป็นเรื่องง่าย:
interface ICloneable
{
public this Clone(); // syntax I invented for a MyType feature
}
ในภาษาที่มีความแตกต่างหลากหลายคุณสามารถทำสิ่งนี้แทน:
interface ICloneable<T> where T : ICloneable<T>
{
public T Clone();
}
class Foo : ICloneable<Foo>
{
public Foo Clone()
{
// …
}
}
โปรดทราบว่านี่ไม่ปลอดภัยเท่ารุ่น MyType เพราะไม่มีอะไรหยุดใครบางคนจากการส่งผ่านคลาส "ผิด" ไปยังตัวสร้างประเภท:
class EvilBar : ICloneable<SomethingTotallyUnrelatedToBar>
{
public SomethingTotallyUnrelatedToBar Clone()
{
// …
}
}
ประเภทสมาชิกบทคัดย่อ
ตามที่ปรากฎถ้าคุณมีสมาชิกประเภทนามธรรมและ subtyping คุณสามารถได้รับโดยสมบูรณ์โดยไม่ต้องมีตัวแปรหลากหลายและยังคงทำสิ่งเดียวกันทั้งหมด Scala กำลังมุ่งหน้าไปในทิศทางนี้เป็นภาษาหลักแรกที่เริ่มต้นด้วยชื่อสามัญและจากนั้นพยายามที่จะลบออกซึ่งเป็นวิธีอื่น ๆ เช่น Java และ C #
โดยทั่วไปใน Scala เช่นเดียวกับคุณสามารถมีเขตข้อมูลและคุณสมบัติและวิธีการเป็นสมาชิกคุณสามารถมีประเภท และเช่นเดียวกับฟิลด์และคุณสมบัติและวิธีการที่สามารถปล่อยให้เป็นนามธรรมเพื่อนำไปใช้ในคลาสย่อยในภายหลังสมาชิกประเภทยังสามารถเป็นนามธรรมได้ ลองกลับไปที่คอลเล็กชันง่ายๆList
ว่าจะมีหน้าตาแบบนี้ถ้ามันรองรับใน C #:
class List
{
T; // syntax I invented for an abstract type member
T Get(int index) { /* … */ }
void Add(T obj) { /* … */ }
}
class IntList : List
{
T = int;
}
// this is equivalent to saying `List<int>` with generics
interface IFoo<T> where T : IFoo<T>
ยัง เห็นได้ชัดว่าเป็นแอปพลิเคชันในชีวิตจริง ตัวอย่างที่ดี แต่ด้วยเหตุผลบางอย่างฉันไม่พอใจ ฉันต้องการที่จะได้รับใจของฉันไปรอบ ๆ เมื่อมันเหมาะสมและเมื่อมันไม่ คำตอบที่นี่มีส่วนร่วมในกระบวนการนี้ แต่ฉันยังรู้สึกว่าตัวเองไม่ได้รอบนี้ มันแปลกเพราะปัญหาระดับภาษาไม่ได้รบกวนฉันมานานแล้ว