Chính xác thì “Lớp học đặc biệt” là gì?


114

Sau khi không lấy được một cái gì đó như sau để biên dịch:

public class Gen<T> where T : System.Array
{
}

với lỗi

Một ràng buộc không được là lớp đặc biệt `System.Array '

Tôi bắt đầu tự hỏi, chính xác thì "lớp học đặc biệt" là gì?

Mọi người dường như thường gặp cùng một loại lỗi khi họ chỉ định System.Enumtrong một ràng buộc chung. Tôi nhận được kết quả tương tự với System.Object, System.Delegate, System.MulticastDelegateSystem.ValueTypequá.

Có nhiều người trong số họ? Tôi không thể tìm thấy bất kỳ thông tin nào về "lớp học đặc biệt" trong C #.

Ngoài ra, những gì quá đặc biệt về những lớp học mà chúng ta không thể sử dụng chúng như một loại hạn chế chung chung?


14
Tôi không nghĩ rằng đây là một bản sao trực tiếp. Câu hỏi không phải là "tại sao tôi không thể sử dụng điều này làm ràng buộc", mà là "những lớp đặc biệt này là gì". Tôi đã xem xét những câu hỏi đó và họ chỉ nêu lý do tại sao việc sử dụng làm ràng buộc là vô ích, không giải thích "lớp đặc biệt" thực sự là gì và tại sao nó được coi là đặc biệt.
Adam Houldsworth

2
Theo kinh nghiệm của tôi, các lớp được sử dụng nhưng bạn không thể sử dụng chúng trực tiếp, chỉ ngầm định thông qua cú pháp khác, là các lớp đặc biệt. Enum rơi vào cùng một loại. Chính xác thì điều gì khiến chúng trở nên đặc biệt, tôi không biết.
Lasse V. Karlsen,

@AndyKyersev: câu hỏi đó hơi khác. Tôi đang yêu cầu định nghĩa về "lớp đặc biệt" và / hoặc danh sách toàn diện về những thứ này. Câu hỏi đó chỉ đơn giản hỏi lý do System.Array không thể là một ràng buộc kiểu chung.
Mints97

Từ tài liệu, nó nói rằng "[...] chỉ hệ thống và trình biên dịch mới có thể dẫn xuất một cách rõ ràng từ lớp Array.". Có khả năng đây là điều khiến nó trở thành một lớp đặc biệt - nó được trình biên dịch đối xử đặc biệt.
RB.

1
@RB: sai rồi. Logic này có nghĩa System.Objectkhông một "lớp học đặc biệt", vì đây là hợp lệ: public class X : System.Object { }, nhưng System.Objectvẫn là một "lớp học đặc biệt".
Mints97

Câu trả lời:


106

Từ mã nguồn Roslyn, nó giống như một danh sách các loại mã cứng:

switch (type.SpecialType)
{
    case SpecialType.System_Object:
    case SpecialType.System_ValueType:
    case SpecialType.System_Enum:
    case SpecialType.System_Delegate:
    case SpecialType.System_MulticastDelegate:
    case SpecialType.System_Array:
        // "Constraint cannot be special class '{0}'"
        Error(diagnostics, ErrorCode.ERR_SpecialTypeAsBound, syntax, type);
        return false;
}

Nguồn: Binder_Constraints.cs IsValidConstraintType
Tôi đã tìm thấy nó bằng cách sử dụng tìm kiếm GitHub: "Một ràng buộc không thể là lớp đặc biệt"


1
@kobi 702 trở thành lỗi trình biên dịch CS0702, như đã thấy trong đầu ra của trình biên dịch (mà câu hỏi này đã bỏ qua để trích dẫn) và các câu trả lời khác.
AakashM

1
@AakashM - Cảm ơn! Tôi đã cố gắng biên dịch và không nhận được số lỗi, vì một số lý do. Sau đó, tôi mất gần 5 phút để tìm hiểu và không có đủ thời gian để chỉnh sửa nhận xét của mình. Câu chuyện buồn.
Kobi

1
@Kobi: bạn phải nhìn vào đầu ra -window, ở đó bạn tìm thấy mã lỗi trình biên dịch chính xác CS0702.
Tim Schmelter,

9
Vì vậy, bây giờ câu hỏi thực sự là tại sao lại có những lớp học đặc biệt này?
David nói Hãy phục hồi Monica vào

@DavidGrinberg Có thể lý do là bạn không thể kế thừa trực tiếp từ các loại này (ngoại trừ object), hoặc ít nhất là nó có liên quan đến nó. Cũng where T : Arraysẽ cho phép chuyển Assay dưới dạng T, đây có lẽ không phải là điều mà hầu hết mọi người muốn.
IllidanS4 muốn Monica trở lại 23/02/16

42

Tôi tìm thấy một bình luận của Jon Skeet từ năm 2008 về một câu hỏi tương tự: Tại sao System.Enumràng buộc không được hỗ trợ.

Tôi biết điều này hơi lạc đề , nhưng anh ấy đã hỏi Eric Lippert (nhóm C #) về nó và họ đã cung cấp câu trả lời sau:

Trước hết, phỏng đoán của bạn là đúng; các hạn chế về các ràng buộc là bởi và lớn là đồ tạo tác của ngôn ngữ, chứ không phải CLR nhiều. (Nếu chúng tôi thực hiện các tính năng này, sẽ có một số điều nhỏ mà chúng tôi muốn thay đổi trong CLR về cách chỉ định các kiểu liệt kê, nhưng chủ yếu đây sẽ là công việc ngôn ngữ.)

Thứ hai, cá nhân tôi muốn có các ràng buộc ủy quyền, ràng buộc enum và khả năng chỉ định các ràng buộc là bất hợp pháp ngày nay bởi vì trình biên dịch đang cố gắng cứu bạn khỏi chính bạn. (Đó là, làm cho các loại niêm phong trở thành hợp pháp như những ràng buộc, v.v.)

Tuy nhiên, do các hạn chế về lịch trình, chúng tôi có thể sẽ không thể đưa các tính năng này vào phiên bản tiếp theo của ngôn ngữ.


10
@YuvalItzchakov - Trích dẫn Github \ MSDN có tốt hơn không? Nhóm C # đã đưa ra một câu trả lời cụ thể về vấn đề này hoặc một câu trả lời tương tự.. Nó thực sự không thể làm tổn thương bất kỳ ai. Jon Skeet chỉ trích dẫn họ và là khá đáng tin cậy khi nó được cho C # ..
Amir Popovich

5
Không cần phải bực bội. Tôi không có ý đây không phải là một câu trả lời hợp lệ :) Được chỉ chia sẻ suy nghĩ của tôi trên nền tảng đó là jonskeet; p
Yuval Itzchakov

40
FYI BTW Tôi nghĩ đó là tôi mà bạn đang trích dẫn ở đó. :-)
Eric Lippert

2
@EricLippert - Điều đó làm cho báo giá thậm chí còn đáng tin cậy hơn.
Amir Popovich

Tên miền của liên kết trong câu trả lời đã chết.
Pang

25

Theo MSDN, đó là một danh sách tĩnh của các lớp:

Lỗi trình biên dịch CS0702

Ràng buộc không được là 'định danh' lớp đặc biệt Các loại sau không được dùng làm ràng buộc:

  • System.Object
  • System.Array
  • System.Delegate
  • System.Enum
  • System.ValueType.

4
Tuyệt vời, có vẻ như là câu trả lời đúng, tìm thấy tốt! Nhưng đâu System.MulticastDelegatetrong danh sách?
Mints97

8
@ Mints97: không có ý kiến, có lẽ thiếu tài liệu?
Tim Schmelter,

Có vẻ như bạn cũng không thể kế thừa từ các lớp này.
David Klempfner

14

Theo Đặc tả ngôn ngữ C # 4.0 (Mã hóa: [10.1.5] Ràng buộc tham số kiểu) cho biết hai điều:

1] Loại không được là đối tượng. Bởi vì tất cả các kiểu bắt nguồn từ đối tượng, một ràng buộc như vậy sẽ không có hiệu lực nếu nó được cho phép.

2] Nếu T không có ràng buộc chính hoặc ràng buộc tham số kiểu thì lớp cơ sở hiệu quả của nó là đối tượng.

Khi bạn xác định một lớp chung, bạn có thể áp dụng các hạn chế đối với các loại kiểu mà mã máy khách có thể sử dụng cho các đối số kiểu khi nó khởi tạo lớp của bạn. Nếu mã máy khách cố gắng khởi tạo lớp của bạn bằng cách sử dụng kiểu không được cho phép bởi một ràng buộc, kết quả là lỗi thời gian biên dịch. Những hạn chế này được gọi là ràng buộc. Các ràng buộc được chỉ định bằng cách sử dụng từ khóa where theo ngữ cảnh. Nếu bạn muốn giới hạn một kiểu chung là một kiểu tham chiếu, hãy sử dụng: class.

public class Gen<T> where T : class
{
}

Điều này sẽ cấm kiểu chung chung là kiểu giá trị, chẳng hạn như int hoặc struct, v.v.

Ngoài ra, Ràng buộc không thể là 'định danh' lớp đặc biệt Các loại sau không được dùng làm ràng buộc:

  • System.Object
  • System.Array
  • System.Delegate
  • System.Enum
  • System.ValueType.

12

Có một số lớp nhất định trong Khung có hiệu quả truyền các đặc tính đặc biệt cho tất cả các kiểu bắt nguồn từ chúng nhưng bản thân không sở hữu các đặc điểm đó . Bản thân CLR không cấm sử dụng các lớp đó làm ràng buộc, nhưng các kiểu chung bị ràng buộc với chúng sẽ không có được các đặc tính không kế thừa như cách các kiểu cụ thể sẽ làm. Những người tạo ra C # đã quyết định rằng vì hành vi như vậy có thể khiến một số người bối rối và họ không thấy bất kỳ tính hữu ích nào của nó, họ nên cấm những ràng buộc như vậy thay vì cho phép họ hành xử như họ làm trong CLR.

Nếu, ví dụ, một được phép viết: void CopyArray<T>(T dest, T source, int start, int count); người ta có thể truyền destsourceđến các phương thức mong đợi một đối số kiểu System.Array; hơn nữa, người ta sẽ nhận được xác thực thời gian biên dịch destsourcelà các kiểu mảng tương thích, nhưng người ta sẽ không thể truy cập các phần tử của mảng bằng []toán tử.

Việc không thể sử dụng Arraynhư một ràng buộc chủ yếu là khá dễ dàng để khắc phục, vì void CopyArray<T>(T[] dest, T[] source, int start, int count)sẽ hoạt động trong hầu hết các tình huống mà phương pháp cũ sẽ hoạt động. Tuy nhiên, nó có một điểm yếu: phương thức trước đây sẽ hoạt động trong trường hợp một hoặc cả hai đối số là kiểu System.Arraytrong khi từ chối các trường hợp mà các đối số là kiểu mảng không tương thích; thêm một quá tải trong đó cả hai đối số đều thuộc loại System.Arraysẽ làm cho mã chấp nhận các trường hợp bổ sung mà nó phải chấp nhận, nhưng cũng làm cho nó chấp nhận sai các trường hợp không nên.

Tôi thấy quyết định đặt ra ngoài vòng pháp luật hầu hết các ràng buộc đặc biệt là khó chịu. Ý nghĩa duy nhất không có ý nghĩa ngữ nghĩa sẽ là System.Object[vì nếu điều đó là hợp pháp như một ràng buộc, thì bất cứ điều gì sẽ thỏa mãn nó]. System.ValueTypecó lẽ sẽ không hữu ích lắm, vì kiểu tham chiếu ValueTypekhông thực sự có nhiều điểm chung với kiểu giá trị, nhưng hợp lý thì nó có thể có một số giá trị trong các trường hợp liên quan đến Phản chiếu. Cả hai System.EnumSystem.Delegatesẽ có một số công dụng thực sự, nhưng vì những người tạo ra C # không nghĩ về chúng nên chúng nằm ngoài vòng pháp luật mà không có lý do chính đáng.


10

Những điều sau có thể được tìm thấy trong CLR thông qua C # 4th Edition:

Ràng buộc chính

Một tham số kiểu có thể chỉ định không có ràng buộc chính hoặc một ràng buộc chính. Một ràng buộc chính có thể là một kiểu tham chiếu xác định một lớp không được niêm phong. Bạn không thể chỉ định một trong các kiểu tham chiếu đặc biệt sau: System.Object , System.Array , System.Delegate , System.MulticastDelegate , System.ValueType , System.Enum hoặc System.Void . Khi chỉ định ràng buộc kiểu tham chiếu, bạn đang hứa với trình biên dịch rằng một đối số kiểu được chỉ định sẽ có cùng kiểu hoặc cùng kiểu dẫn xuất từ ​​kiểu ràng buộc.


Xem thêm: C # LS phần 10.1.4.1: Các lớp cơ sở trực tiếp của một kiểu lớp không phải là một trong các loại sau đây: System.Array, System.Delegate, System.MulticastDelegate, System.Enum, hoặc System.ValueType. Hơn nữa, một khai báo lớp chung không thể sử dụng System.Attributenhư một lớp cơ sở trực tiếp hoặc gián tiếp.
Jeroen Vannevel

5

Tôi không nghĩ rằng tồn tại bất kỳ định nghĩa chính thức nào về "lớp đặc biệt" / "loại đặc biệt".

Bạn có thể nghĩ về chúng aa loại, không thể được sử dụng với ngữ nghĩa của loại "thông thường":

  • bạn không thể khởi tạo chúng trực tiếp;
  • bạn không thể kế thừa loại tùy chỉnh từ chúng trực tiếp;
  • có một số phép thuật biên dịch để làm việc với chúng (tùy chọn);
  • việc sử dụng trực tiếp các phiên bản của chúng ít nhất là vô ích (tùy chọn; hãy tưởng tượng, bạn đã tạo mã chung ở trên, bạn sẽ viết mã chung nào?)

Tái bút tôi muốn thêm System.Voidvào danh sách.


2
System.Voidđưa ra một lỗi hoàn toàn khác nhau khi sử dụng như một hạn chế chung =)
Mints97

@ Mints97: đúng. Nhưng nếu câu hỏi là về "đặc biệt", thì vâng, voidlà rất đặc biệt. :)
Dennis

@Dennis: Mã có một vài tham số của kiểu bị hạn chế để System.Arraycó thể sử dụng các phương thức như Array.Copydi chuyển dữ liệu từ cái này sang cái khác; mã với các tham số của một kiểu bị hạn chế System.Delegatesẽ có thể sử dụng Delegate.Combinetrên chúng và truyền kết quả đến kiểu thích hợp . Việc sử dụng hiệu quả một loại đã biết chung Enumsẽ sử dụng Phản chiếu một lần cho mỗi loại như vậy, nhưng một HasAnyFlagphương pháp chung có thể nhanh hơn 10 lần so với một phương pháp không chung chung.
supercat
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.