2010-05-24 32 views
11

Trong C# Tôi đang cố gắng viết mã, nơi tôi sẽ tạo ra một đại biểu Func đó là trong chính nó chung chung. Ví dụ như sau (không Generic) đại biểu được trả lại một chuỗi tùy ý:C# Generic Generics (Một câu hỏi nghiêm túc)

Func<string> getString =() => "Hello!"; 

tôi, mặt khác muốn tạo một generic có vai trò tương tự như phương pháp chung. Ví dụ nếu tôi muốn có một Func tổng quát để trở lại mặc định (T) cho một loại T. Tôi tưởng tượng rằng tôi viết code như sau:

Func<T><T> getDefaultObject = <T>() => default(T); 

Sau đó, tôi sẽ sử dụng nó như

getDefaultObject<string>() đó sẽ quay trở lại null và nếu tôi viết getDefaultObject<int>() sẽ trả về 0.

Câu hỏi này không chỉ đơn thuần là một bài tập học thuật. Tôi đã tìm thấy rất nhiều nơi mà tôi có thể đã sử dụng điều này nhưng tôi không thể có được cú pháp đúng. Điều này có thể không? Có thư viện nào cung cấp loại chức năng này không?

+5

Có một số lý do mà 'Func CreateGetDefaultObject () {return() => mặc định (T); } 'sẽ không hoạt động? –

+0

Tôi thấy bây giờ. Bạn muốn chuyển một đối số kiểu biên dịch cho một đại biểu. –

Trả lời

4

Mặc dù người ta có thể tìm thấy thực tế cách giải quyết như Stephen Cleary của

Func<T> CreateGetDefaultObject<T>() { return() => default(T); } 

nơi bạn có thể chỉ định các thuốc generic trực tiếp, đây là một vấn đề khá thú vị từ một quan điểm lý thuyết mà không thể được giải quyết bằng cách loại hiện tại C# 's hệ thống.


Một loại đó, như bạn gọi nó, là của riêng mình generic, được gọi là một loại -rank cao.

Hãy xem xét ví dụ sau (pseudo-C#):

Tuple<int[], string[]> Test(Func<?> f) { 
    return (f(1), f("Hello")); 
} 

Trong hệ thống đề xuất của bạn, một cuộc gọi có thể nhìn như thế:

Test(x => new[] { x }); // Returns ({ 1 }, { "Hello" }) 

Nhưng câu hỏi là: Làm thế nào để chúng ta gõ chức năng Test và đối số của nó là f? Dường như, f bản đồ mọi loại T thành mảng T[] thuộc loại này. Vì vậy, có thể?

Tuple<int[], string[]> Test<T>(Func<T, T[]> f) { 
    return (f(1), f("Hello")); 
} 

Nhưng điều này không hoạt động. Chúng tôi không thể tham số hóa Test với bất kỳ cụ thểT, kể từ f có thể được áp dụng cho tất cả loại T. Tại thời điểm này, hệ thống kiểu C# không thể đi xa hơn.

Những gì chúng ta cần là một ký hiệu như

Tuple<int[], string[]> Test(forall T : Func<T, T[]> f) { 
    return (f(1), f("Hello")); 
} 

Trong trường hợp của bạn, bạn có thể gõ

forall T : Func<T> getDefaultValue = ... 

Ngôn ngữ duy nhất mà tôi biết có hỗ trợ loại Generics là Haskell:

test :: (forall t . t -> [t]) -> ([Int], [String]) 
test f = (f 1, f "hello") 

Xem mục nhập Haskellwiki này trên polymorphism về ký hiệu forall này.

+0

Cảm ơn Dario, câu trả lời rất thú vị. Mặc dù tôi không phải là một bậc thầy Haskell bởi bất kỳ căng, tôi hiểu câu trả lời của bạn và liên kết bạn đã cung cấp. Theo như tôi hiểu, không có giải pháp/giải pháp nào vì đây là giới hạn của ngôn ngữ vì nó không hỗ trợ Hạng N Loại. Có lẽ trong 5 - 10 năm nữa, Microsoft sẽ thêm "Rank n Types" vào CLR. –

+0

@tahirhassan: Đúng, đó là một thuộc tính khá cơ bản của hệ thống kiểu ngôn ngữ (và một cái khá tiên tiến - thậm chí Haskell không chỉ hỗ trợ nó bằng các phần mở rộng không chuẩn) mà người ta không thể chạy nhanh hơn. Một khả năng tôi thấy mặc dù bạn có thể phân loại mô phỏng hành vi này bằng cách thực hiện một giao diện như 'giao diện ToRankNArray {T [] Áp dụng (T arg); } '. Các ngôn ngữ như Java hoặc F # cho phép tạo các kiểu con ẩn danh, nhưng tất nhiên nó không thuận tiện như một hàm nặc danh cũng không thay đổi chính bản thân kiểu hệ thống. – Dario

-1

Bạn không thể làm điều này, vì thông số loại chung phải được biết khi chạy. Bạn phải sử dụng lớp activator:

Object o = Activator.CreateInstance(typeof(StringBuilder));

mà sẽ làm chính xác những gì bạn muốn. Bạn có thể viết nó như sau:

public T Default<T>() 
{ 
    return (T)Activator.CreateInstance(typeof(T)); 
} 

Sửa

giải pháp Blindy là tốt hơn.

8

Vâng, bạn không thể quá tải bất cứ điều gì chỉ dựa trên giá trị trả về, do đó, điều này bao gồm các biến.

Bạn tuy nhiên có thể thoát khỏi điều đó biểu thức lambda và viết một hàm thực:

T getDefaultObject<T>() { return default(T); } 

và sau đó bạn gọi nó là chính xác như bạn muốn:

int i=getDefaultObject<int>();  // i=0 
string s=getDefaultObject<string>(); // s=null 
+0

Cảm ơn câu trả lời Blindy, nhưng câu hỏi đặt ra là tự động thiết lập Generic Generic Delegate (nếu điều đó là có thể). Vấn đề với mã của bạn là bạn đang tạo một phương thức chứ không phải là một đại biểu có thể khai báo, khởi tạo và sử dụng WITHIN một phương thức. –

+0

Đúng, nhưng không trả lời câu hỏi. – Dario

0

này là không thể, kể từ một đại biểu thể hiện trong C# không thể có thông số chung. Gần nhất bạn có thể nhận được là chuyển đối tượng kiểu như là một tham số thông thường và sử dụng sự phản chiếu. :(

Trong nhiều trường hợp, đúc để động giúp loại bỏ nỗi đau của sự phản ánh, nhưng động không giúp khi tạo trường mới, chẳng hạn như ví dụ của bạn.