2016-09-12 18 views
10

Trong mã mẫu được trình bày bên dưới, phương thức "CompileError" sẽ không biên dịch, vì nó yêu cầu ràng buộc where T : new() như được hiển thị trong phương thức CreateWithNew(). Tuy nhiên, phương thức CreateWithActivator<T>() chỉ biên dịch mà không bị ràng buộc.Tại sao Activator.CreateInstance <T>() được phép không có ràng buộc kiểu generic() chung mới?

public class GenericTests 
{ 
    public T CompileError<T>() // compile error CS0304 
    { 
     return new T(); 
    } 

    public T CreateWithNew<T>() where T : new() // builds ok 
    { 
     return new T(); 
    } 

    public T CreateWithActivator<T>() // builds ok 
    { 
     return Activator.CreateInstance<T>(); 
    } 
} 

Tại sao điều này?

Theo https://stackoverflow.com/a/1649108/531971, có sự tham khảo MSDN documentation, và this question, khái niệm new T() trong Generics là thực sự thực hiện sử dụng Activator.CreateInstance<T>(). Vì vậy, tôi không hiểu tại sao gọi số new T() yêu cầu loại chung được hạn chế theo cách có thể bỏ qua khi sử dụng Activator.CreateInstance<T>().

Hoặc, để đặt câu hỏi theo cách khác: điểm của ràng buộc where T : new() là gì, nếu dễ dàng tạo các phiên bản T trong một phương pháp chung mà không có ràng buộc, bằng cách trực tiếp sử dụng cơ sở hạ tầng cơ bản giống nhau?

+1

Vì 'CreateInstance()' chỉ sử dụng sự phản chiếu cũ đơn giản và không chịu bất kỳ ràng buộc nào. Nếu kiểu có một hàm tạo mặc định, nó sẽ tạo ra nó. –

+1

Đây là điều tự kiểm soát. Bạn có thể làm khá nhiều thứ thông qua sự phản chiếu (thậm chí tiêu diệt tất cả các nguyên tắc OOP), nhưng nhiều người sẽ không đánh giá cao nó, gây ra sự bẩn thỉu của nó. – eocron

+2

Có rất nhiều cách viết mã vụng về cho phép bạn loại bỏ một lỗi thời gian biên dịch và tạo ra một thời gian chạy thay thế. Đây chỉ là một ví dụ. Ví dụ. 'dynamic' cho phép bạn viết các lời gọi đến các hàm không tồn tại, do đó, thay vì một lỗi trình biên dịch, bạn nhận được một ngoại lệ thời gian chạy. Tương tự như vậy, ở đây, 'CreateInstance' cho phép bạn trì hoãn việc tìm ra rằng không có hàm tạo tham số nào tồn tại cho đến khi chạy. –

Trả lời

12

Có sự khác biệt về khái niệm giữa ActivatorT():

  • Activator.CreateInstance<T> - Tôi muốn tạo ra một thể hiện mới của T sử dụng constructor mặc định của nó - Và ném một Exception nếu nó không có một (Vì có điều gì đó rất sai đã xảy ra và tôi muốn xử lý nó/tự ném nó).

    • Side lưu ý: ghi nhớ rằng as MSDN says:

      Nói chung, không có sử dụng cho CreateInstance<T>() phương pháp chung trong mã ứng dụng, vì loại phải được biết tại thời gian biên dịch. Nếu loại được biết tại thời gian biên dịch, cú pháp instantiation bình thường có thể được sử dụng.

      từ thường bạn sẽ muốn sử dụng một constructor khi Type được biết đến tại thời gian biên dịch (CreateInstance<T>()uses RuntimeTypeHandle.CreateInstance mà là chậm hơn [Ngoài ra đó là lý do tại sao Activator.CreateInstance<T> tự nó không cần hạn chế]).

  • T() - I Want to gọi constructor rỗng của T, được cho là giống như một cuộc gọi constructor chuẩn.

Bạn không muốn một "tiêu chuẩn" gọi constructor thất bại vì "Không xây dựng như đã được tìm thấy", do đó, trình biên dịch muốn bạn hạn chế rằng có một.

Hơn thế nữa; Bạn nên sử dụng các lỗi thời gian Biên dịch trên Exceptions nếu có thể.

Thực tế là T() được thực hiện trong nội bộ sử dụng phản ánh không thích hợp cho trường hợp trung bình của "Tôi chỉ muốn có một trường hợp mặc định của T" (tất nhiên rằng việc thực hiện nội bộ là rất quan trọng nếu bạn quan tâm đến hiệu suất/etc ...).

2

Đây chỉ là đường. Tự kiểm soát đường nếu bạn có thể nói như vậy. Ví dụ, bạn có thể gọi qua phản xạ hầu như bất kỳ phương thức nào trong bất kỳ kiểu nào (trong một số trường hợp thậm chí không có trường hợp!), Nhưng điều này không đúng, bạn có đồng ý không? Mã của bạn sẽ trở thành không thể duy trì tại một số điểm, và nhiều lỗi sẽ bật lên trong thời gian thực hiện, điều này là rất xấu. Vì vậy, nếu bạn có thể kiểm soát bản thân trước khi thực hiện - chỉ cần làm điều đó.

Hạn chế sẽ giúp bạn hiểu điều đó trong thời gian biên dịch.

1

Phương pháp Activator.CreateInstance<T>() được hiển thị với mã người dùng để cho phép khả năng một lớp chung có thể sử dụng theo nhiều cách khác nhau, một số yêu cầu tham số kiểu đáp ứng một số ràng buộc nhất định và một số không. Ví dụ, một lớp Foo<T> có thể hỗ trợ bất kỳ mô hình sử dụng sau đây:

  1. Khách hàng mã cung cấp một hàm trả về một mới T.

  2. Mã khách hàng sẽ chuyển sang chức năng mặc định, tạo một T mới bằng cách sử dụng hàm tạo mặc định của nó.

  3. Mã khách hàng tránh sử dụng bất kỳ tính năng nào của lớp yêu cầu nó để tạo các phiên bản mới T.

Patterns # 1 và # 3 nên được sử dụng với bất kỳ T, trong khi # 2 chỉ nên làm việc với các loại có nhà xây dựng parameterless. Có Activator.CreateInstance<T>() biên dịch cho T không bị giới hạn và làm việc cho các loại T xảy ra với các nhà thầu không có tham số, giúp dễ dàng có mã hỗ trợ cả ba mẫu sử dụng. Nếu Activator.CreateInstance<T> có giới hạn new, sẽ rất khó xử khi sử dụng nó với các thông số loại chung mà không có.

Các vấn đề liên quan