Trước tiên chúng ta hãy nói về đa hình tham số thuần túy và đi vào đa hình giới hạn sau.
Đa hình tham số có nghĩa là gì? Vâng, điều đó có nghĩa là một kiểu, hay đúng hơn là hàm tạo được tham số hóa bởi một kiểu. Vì loại được truyền vào dưới dạng tham số, bạn không thể biết trước nó có thể là gì. Bạn không thể đưa ra bất kỳ giả định nào dựa trên nó. Bây giờ, nếu bạn không biết nó có thể là gì, thì công dụng của nó là gì? bạn có thể làm gì với nó?
Vâng, bạn có thể lưu trữ và lấy nó, ví dụ. Đó là trường hợp bạn đã đề cập: bộ sưu tập. Để lưu trữ một mục trong danh sách hoặc một mảng, tôi không cần biết gì về mục đó. Danh sách hoặc mảng có thể hoàn toàn không biết về loại.
Nhưng còn Maybe
loại nào? Nếu bạn không quen thuộc với nó, Maybe
là một loại có thể có giá trị và có thể không. Bạn sẽ sử dụng nó ở đâu? Chà, ví dụ, khi lấy một mục ra khỏi từ điển: thực tế là một mục có thể không có trong từ điển không phải là một tình huống đặc biệt, vì vậy bạn thực sự không nên ném ngoại lệ nếu mục đó không có. Thay vào đó, bạn trả về một thể hiện của một kiểu con Maybe<T>
, trong đó có chính xác hai kiểu con: None
và Some<T>
. int.Parse
là một ứng cử viên khác của một cái gì đó thực sự nên trả lại Maybe<int>
thay vì ném một ngoại lệ hoặc toàn bộ int.TryParse(out bla)
điệu nhảy.
Bây giờ, bạn có thể lập luận rằng đó Maybe
là một loại giống như một danh sách chỉ có thể có 0 hoặc một phần tử. Và do đó, sắp xếp một bộ sưu tập.
Vậy thì Task<T>
sao? Đây là một loại hứa hẹn sẽ trả lại một giá trị tại một thời điểm nào đó trong tương lai, nhưng không nhất thiết phải có một giá trị ngay bây giờ.
Hay những gì về Func<T, …>
? Làm thế nào bạn đại diện cho khái niệm của một chức năng từ loại này sang loại khác nếu bạn không thể trừu tượng hơn các loại?
Hoặc, nói chung hơn: coi việc trừu tượng hóa và tái sử dụng là hai hoạt động cơ bản của công nghệ phần mềm, tại sao bạn không muốn có thể trừu tượng hóa các loại?
Vì vậy, bây giờ hãy nói về đa hình giới hạn. Đa hình giới hạn về cơ bản là ở đó đa hình tham số và đa hình phụ gặp nhau: thay vì một hàm tạo kiểu hoàn toàn không biết về tham số kiểu của nó, bạn có thể liên kết (hoặc ràng buộc) loại thành một kiểu con của một số loại được chỉ định.
Hãy quay trở lại bộ sưu tập. Hãy băm. Chúng tôi đã nói ở trên rằng một danh sách không cần biết gì về các yếu tố của nó. Chà, một hashtable nào: nó cần biết rằng nó có thể băm chúng. (Lưu ý: trong C #, tất cả các đối tượng đều có thể băm, giống như tất cả các đối tượng có thể được so sánh bằng nhau. Tuy nhiên, điều đó không đúng với tất cả các ngôn ngữ và đôi khi được coi là lỗi thiết kế ngay cả trong C #.)
Vì vậy, bạn muốn giới hạn tham số loại của mình cho loại khóa trong hashtable là một thể hiện của IHashable
:
class HashTable<K, V> where K : IHashable
{
Maybe<V> Get(K key);
bool Add(K key, V value);
}
Hãy tưởng tượng nếu thay vào đó bạn có điều này:
class HashTable
{
object Get(IHashable key);
bool Add(IHashable key, object value);
}
Bạn sẽ làm gì với một value
bạn ra khỏi đó? Bạn không thể làm bất cứ điều gì với nó, bạn chỉ biết đó là một đối tượng. Và nếu bạn lặp đi lặp lại, tất cả những gì bạn nhận được là một cặp thứ bạn biết là một thứ IHashable
(nó không giúp bạn nhiều vì nó chỉ có một tài sản Hash
) và thứ bạn biết là object
(giúp bạn thậm chí ít hơn).
Hoặc một cái gì đó dựa trên ví dụ của bạn:
class Repository<T> where T : ISerializable
{
T Get(int id);
void Save(T obj);
void Delete(T obj);
}
Mục này cần được tuần tự hóa vì nó sẽ được lưu trữ trên đĩa. Nhưng nếu bạn có cái này thay thế:
class Repository
{
ISerializable Get(int id);
void Save(ISerializable obj);
void Delete(ISerializable obj);
}
Với trường hợp chung chung, nếu bạn đặt một BankAccount
trong, bạn sẽ có được một BankAccount
trở lại, với các phương pháp và các thuộc tính thích Owner
, AccountNumber
, Balance
, Deposit
, Withdraw
,, vv Một cái gì đó bạn có thể làm việc với. Bây giờ, trường hợp khác? Bạn đặt vào BankAccount
nhưng bạn nhận lại a Serializable
, chỉ có một thuộc tính : AsString
. Bạn sẽ làm gì với điều đó?
Ngoài ra còn có một số thủ thuật gọn gàng bạn có thể làm với đa hình bị ràng buộc:
Định lượng giới hạn F về cơ bản là ở đó biến loại xuất hiện lại trong ràng buộc. Điều này có thể hữu ích trong một số trường hợp. Ví dụ, làm thế nào để bạn viết một ICloneable
giao diện? Làm thế nào để bạn viết một phương thức trong đó kiểu trả về là kiểu của lớp triển khai? Trong một ngôn ngữ có tính năng MyType , thật dễ dàng:
interface ICloneable
{
public this Clone(); // syntax I invented for a MyType feature
}
Trong một ngôn ngữ có tính đa hình giới hạn, bạn có thể làm một cái gì đó như thế này:
interface ICloneable<T> where T : ICloneable<T>
{
public T Clone();
}
class Foo : ICloneable<Foo>
{
public Foo Clone()
{
// …
}
}
Lưu ý rằng điều này không an toàn như phiên bản MyType, vì không có gì ngăn ai đó chỉ đơn giản chuyển lớp "sai" sang hàm tạo kiểu:
class EvilBar : ICloneable<SomethingTotallyUnrelatedToBar>
{
public SomethingTotallyUnrelatedToBar Clone()
{
// …
}
}
Thành viên loại trừu tượng
Hóa ra, nếu bạn có các thành viên loại trừu tượng và phân nhóm, bạn thực sự có thể có được hoàn toàn mà không cần đa hình tham số và vẫn làm tất cả những điều tương tự. Scala đang đi theo hướng này, là ngôn ngữ chính đầu tiên bắt đầu với khái quát và sau đó cố gắng loại bỏ chúng, đó chính xác là cách khác xung quanh từ ví dụ Java và C #.
Về cơ bản, trong Scala, giống như bạn có thể có các trường và thuộc tính và phương thức làm thành viên, bạn cũng có thể có các loại. Và giống như các trường và các thuộc tính và phương thức có thể được để lại trừu tượng để được triển khai trong một lớp con sau này, các thành viên kiểu cũng có thể được để lại trừu tượng. Chúng ta hãy quay trở lại các bộ sưu tập, một cách đơn giản List
, sẽ trông giống như thế này, nếu nó được hỗ trợ trong 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>
cũng nhớ về . Đó rõ ràng là ứng dụng thực tế. ví dụ rất tuyệt nhưng vì một số lý do tôi không cảm thấy hài lòng tôi muốn có được tâm trí của tôi khi nó phù hợp và khi nó không. câu trả lời ở đây có một số đóng góp cho quá trình này, nhưng tôi vẫn cảm thấy bản thân không hài lòng với tất cả những điều này. Thật lạ vì các vấn đề ở cấp độ ngôn ngữ không làm phiền tôi quá lâu.