Có cách tiếp cận hợp lý nào đối với các tham số kiểu "mặc định" trong C # Generics không?


91

Trong các mẫu C ++, người ta có thể chỉ định rằng một tham số kiểu nhất định là một tham số mặc định. Tức là trừ khi được chỉ định rõ ràng, nó sẽ sử dụng kiểu T.

Điều này có thể được thực hiện hoặc gần đúng trong C #?

Tôi đang tìm kiếm một cái gì đó như:

public class MyTemplate<T1, T2=string> {}

Vì vậy, một phiên bản của kiểu không chỉ định rõ ràng T2:

MyTemplate<int> t = new MyTemplate<int>();

Về cơ bản sẽ là:

MyTemplate<int, string> t = new MyTemplate<int, string>();

Cuối cùng, tôi đang xem xét một trường hợp trong đó có một mẫu được sử dụng khá rộng rãi, nhưng tôi đang xem xét mở rộng với một tham số kiểu bổ sung. Tôi có thể phân loại phụ, tôi đoán vậy, nhưng tôi tò mò liệu có các lựa chọn khác trong mạch này không.

Câu trả lời:


76

Phân lớp là lựa chọn tốt nhất.

Tôi sẽ phân lớp phụ lớp chung chung chính của bạn:

class BaseGeneric<T,U>

với một lớp cụ thể

class MyGeneric<T> : BaseGeneric<T, string>

Điều này giúp dễ dàng giữ logic của bạn ở một nơi (lớp cơ sở), nhưng cũng dễ dàng cung cấp cả hai tùy chọn sử dụng. Tùy thuộc vào lớp học, có thể có rất ít công việc bổ sung cần thiết để thực hiện điều này.


1
à ... có lý đấy. Tên kiểu có được phép giống nhau không nếu các tham số kiểu cung cấp một chữ ký duy nhất?
el2iot2

2
@ee: vâng, tổng quát là overloadabletheo số lượng tham số.
Mehrdad Afshari

@ee: Có, nhưng tôi sẽ cảnh giác khi làm điều đó. Việc làm như vậy là "hợp pháp" trong .NET, nhưng nó có thể dẫn đến nhầm lẫn. Tôi thà đặt tên của kiểu dẫn xuất chuỗi tương tự với lớp chung chung chính (vì vậy rõ ràng nó là gì / dễ tìm), nhưng một cái tên làm rõ ràng rằng đó là một chuỗi.
Reed Copsey

@Reed: Nó có thực sự dẫn đến nhầm lẫn không? Đối với trường hợp cụ thể này, tôi nghĩ rằng có cùng một tên thậm chí còn giúp ích. Có những ví dụ trong .NET làm điều tương tự: Ví dụ: Func <> ủy quyền.
Mehrdad Afshari

4
Ví dụ, Predicate <T> chỉ là Func <T, bool>, nhưng được đổi tên vì nó cho một mục đích khác.
Reed Copsey

19

Một giải pháp là phân lớp. Một phương thức khác mà tôi sẽ sử dụng thay thế, là các phương thức gốc (kết hợp với từ khóa var).

public class MyTemplate<T1,T2>
{
     public MyTemplate(..args..) { ... } // constructor
}

public static class MyTemplate{

     public static MyTemplate<T1,T2> Create<T1,T2>(..args..)
     {
         return new MyTemplate<T1, T2>(... params ...);
     }

     public static MyTemplate<T1, string> Create<T1>(...args...)
     {
         return new MyTemplate<T1, string>(... params ...);
     }
}

var val1 = MyTemplate.Create<int,decimal>();
var val2 = MyTemplate.Create<int>();

Trong ví dụ trên val2là loại MyTemplate<int,string> và không một loại bắt nguồn từ nó.

Một loại class MyStringTemplate<T>:MyTemplate<T,string>không phải là cùng một loại với MyTemplate<T,string>. Điều này có thể gây ra một số vấn đề trong các tình huống nhất định. Ví dụ, bạn không thể truyền một phiên bản của MyTemplate<T,string>tới MyStringTemplate<T>.


3
Đây là cách tiếp cận hữu dụng nhất. Rất thoải mái giải pháp
T-moty

12

bạn cũng có thể tạo một lớp Overload như vậy

public class MyTemplate<T1, T2> {
    public T1 Prop1 { get; set; }
    public T2 Prop2 { get; set; }
}

public class MyTemplate<T1> : MyTemplate<T1, string>{}

Vui lòng đọc các câu trả lời khác trước khi bạn đăng câu trả lời muộn, vì có thể giải pháp của bạn cũng giống như những người khác.
Cheng Chen

7
câu trả lời chấp nhận đã tạo ra một lớp học với tên khác, giải pháp của tôi là quá tải cùng lớp
Nerdroid

2
Không, cả hai đều đang tạo một lớp mới. Tên không quan trọng ở đây. MyTemplate<T1>là một lớp khác với MyTemplate<T1, T2>, không AnotherTemplate<T1>.
Cheng Chen

7
Ngay cả khi các loại thực sự khác nhau, rõ ràng và chính xác, việc mã hóa sẽ dễ dàng hơn nếu bạn giữ cùng một tên. Không phải ai cũng rõ rằng "quá tải" lớp có thể được sử dụng theo cách đó. @DannyChen đúng - từ quan điểm kỹ thuật, kết quả là giống nhau, nhưng câu trả lời này gần đạt được những gì OP yêu cầu hơn.
Kuba

1
Đáng tiếc là giải pháp này vẫn còn thua kém "mặc định" true lập luận chung chung, vì một MyTemplate<T1, string>không thể được gán cho MyTemplate<T1>, mà có thể là desireable
Felk

9

C # không hỗ trợ một tính năng như vậy.

Như bạn đã nói, bạn có thể phân lớp nó (nếu nó không được niêm phong và sao chép tất cả các khai báo của hàm tạo) nhưng đó là một điều hoàn toàn khác.


2

Rất tiếc, C # không hỗ trợ những gì bạn đang cố gắng thực hiện. Sẽ là một tính năng khó thực hiện vì kiểu mặc định cho một tham số sẽ phải tuân theo các ràng buộc chung và rất có thể sẽ gây đau đầu khi CLR cố gắng đảm bảo an toàn kiểu.


1
Không hẳn. Nó có thể được thực hiện với một thuộc tính (như các tham số mặc định trong VB.NET) và để trình biên dịch thay thế nó tại thời điểm biên dịch. Lý do chính là mục tiêu thiết kế C #.
Mehrdad Afshari

Trình biên dịch phải đảm bảo rằng tham số mặc định thỏa mãn các ràng buộc chung. Ngoài ra, tham số mặc định sẽ là một ràng buộc chung vì bất kỳ giả định nào được thực hiện về tham số kiểu trong phương thức sẽ yêu cầu bất kỳ tham số kiểu không mặc định nào phải kế thừa từ đó.
Andrew Hare

@Andrew, tham số mặc định không cần phải là một ràng buộc chung. Nếu điều này hoạt động giống với các tham số mẫu mặc định trong C ++ thì việc mở rộng trên lớp của automatonic sẽ hoàn toàn ổn: MyTemplate <int, float> x = null Vì T2 không có ràng buộc chung, vì vậy float vẫn ổn mặc dù kiểu mặc định của chuỗi . Theo cách này, các tham số mẫu mặc định về cơ bản chỉ là "đường cú pháp" để viết MyTemplate <int> dưới dạng viết tắt cho MyTemplate <int, string>.
Tyler Laing

@Andrew, tôi đồng ý rằng trình biên dịch C # nên xác minh rằng các tham số mẫu mặc định như vậy đáp ứng các hằng số chung hiện có. Trình biên dịch C # đã xác minh điều gì đó tương tự trong khai báo lớp, ví dụ: thêm một ràng buộc chung như "where U: ISomeInterface" vào lớp BaseGeneric <T, U> của Reed và sau đó MyGeneric <T> sẽ không biên dịch được với lỗi. Việc xác minh một tham số mẫu mặc định sẽ rất giống nhau: trình biên dịch xác minh khi khai báo một lớp và thông báo lỗi có thể giống nhau hoặc rất giống nhau.
Tyler Laing

"Đó sẽ là một tính năng khó thực hiện" Điều đó thật vô lý. Nó sẽ là tầm thường để thực hiện. Công việc xác thực một kiểu chống lại các ràng buộc của mẫu không khó hơn vì kiểu ứng cử viên xuất hiện ở một vị trí khác trong tệp (tức là trong khuôn mẫu chứ không phải tại bản tạo mẫu).
Bùn
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.