2010-09-02 36 views
80

Func<> là gì và nó được sử dụng để làm gì?Func là gì, như thế nào và khi nào nó được sử dụng

+2

Đó chỉ là lối tắt cho các đại biểu có chữ ký cụ thể. Để hiểu đầy đủ các câu trả lời dưới đây bạn sẽ cần phải hiểu các đại biểu ;-) –

+1

Trong câu trả lời của @Oded nó nói 'Nếu bạn có một hàm cần trả về các kiểu khác nhau, tùy thuộc vào tham số, bạn có thể sử dụng một đại biểu Func, xác định kiểu trả về.' – Lijo

Trả lời

57

Func<T> là loại đại biểu được xác định trước cho phương thức trả về một số giá trị thuộc loại T.

Nói cách khác, bạn có thể sử dụng loại này để tham chiếu phương thức trả về một số giá trị là T. Ví dụ.

public static string GetMessage() { return "Hello world"; } 

có thể được tham chiếu như thế này

Func<string> f = GetMessage; 
+0

Nhưng nó cũng có thể đại diện cho một hàm đối số tĩnh =) –

+2

@ Ark-kun không, điều đó không chính xác. Định nghĩa của 'Func ' là 'đại biểu TResult Func ()'. Không có đối số. 'Func ' sẽ là một hàm nhận một đối số. –

+3

Không, tôi đúng. 'static int OneArgFunc (chuỗi này i) {return 42; } '' Func f = "foo" .OneArgFunc; '. =) –

9

Func<T1,R> và được xác định trước generic Func đại biểu khác (Func<T1,T2,R>, Func<T1,T2,T3,R> và những người khác) là đại biểu chung chung mà trả về kiểu của tham số chung cuối cùng.

Nếu bạn có hàm cần trả về các loại khác nhau, tùy thuộc vào thông số, bạn có thể sử dụng đại diện Func, chỉ định loại trả về.

66

Hãy coi đó là trình giữ chỗ. Nó có thể khá hữu ích khi bạn có mã theo một mẫu nhất định nhưng không cần phải gắn với bất kỳ chức năng cụ thể nào. Ví dụ, xem xét phương pháp mở rộng Enumerable.Select.

  • Các mẫu là: cho tất cả các mục trong một chuỗi, chọn một số giá trị từ mục đó (ví dụ, một tài sản) và tạo ra một chuỗi mới gồm những giá trị này.
  • Trình giữ chỗ là: một số hàm chọn thực sự nhận được các giá trị cho chuỗi được mô tả ở trên.

Phương pháp này mất Func<T, TResult> thay vì bất kỳ chức năng cụ thể nào. Điều này cho phép nó được sử dụng trong bất kỳ ngữ cảnh nào khi áp dụng mẫu trên.

Ví dụ: giả sử tôi có số List<Person> và tôi chỉ muốn tên của mọi người trong danh sách. Tôi có thể làm điều này:

var names = people.Select(p => p.Name); 

Hoặc nói rằng tôi muốn tuổi của tất cả mọi người:

var ages = people.Select(p => p.Age); 

Ngay lập tức, bạn có thể xem như thế nào tôi đã có thể tận dụng các mã cùng đại diện cho một mẫu (với Select) với hai các chức năng khác nhau (p => p.Namep => p.Age).

Cách khác là viết một phiên bản khác nhau Select mỗi lần bạn muốn quét chuỗi cho một loại giá trị khác.Vì vậy, để đạt được hiệu quả tương tự như trên, tôi cần:

// Presumably, the code inside these two methods would look almost identical; 
// the only difference would be the part that actually selects a value 
// based on a Person. 
var names = GetPersonNames(people); 
var ages = GetPersonAges(people); 

Với một đại biểu làm chỗ giữ chỗ, tôi tự do phải viết cùng một mẫu hoa văn trong những trường hợp như thế này.

47

Func<T1, T2, ..., Tn, Tr> đại diện cho một hàm, có các đối số (T1, T2, ..., Tn) và trả về Tr.

Ví dụ, nếu bạn có một hàm:

double sqr(double x) { return x * x; } 

Bạn có thể lưu nó như một số loại của một hàm biến:

Func<double, double> f1 = sqr; 
Func<double, double> f2 = x => x * x; 

Và sau đó sử dụng chính xác như bạn sẽ sử dụng sqr:

f1(2); 
Console.WriteLine(f2(f1(4))); 

, vv

Hãy nhớ rằng, đó là một đại biểu, để biết thêm thông tin nâng cao tham khảo tài liệu.

+0

Câu trả lời Excelent, nhưng để biên dịch từ khóa tĩnh được neeed – boctulus

5

Nó chỉ là một đại biểu được xác định trước. Sử dụng nó, bạn không cần khai báo mọi đại biểu. Có một đại biểu được xác định trước khác, Action<T, T2...>, tương tự nhưng trả về khoảng trống.

3

Tôi thấy Func rất hữu ích khi tạo thành phần cần được cá nhân hóa "khi đang di chuyển".

Lấy ví dụ rất đơn giản này: thành phần PrintListToConsole.

Một đối tượng rất đơn giản in danh sách đối tượng này vào bảng điều khiển. Bạn muốn cho nhà phát triển sử dụng nó để cá nhân hóa đầu ra.

Ví dụ: Bạn muốn cho phép anh ta xác định loại định dạng số dạng hạt và vân vân.

Without Func

Trước tiên, bạn phải tạo một giao diện cho một lớp học mà lấy đầu vào và tạo ra chuỗi in ra cửa sổ Console.

interface PrindListConsoleRender<T> { 
    String Render(T input); 
} 

Sau đó, bạn phải tạo lớp PrintListToConsole lấy giao diện đã tạo trước đó và sử dụng nó trên từng phần tử trong danh sách.

class PrintListToConsole<T> { 

    private PrindListConsoleRender<T> _renderer; 

    public void SetRenderer(PrindListConsoleRender<T> r) { 
     // this is the poin where I can personalize the render mechanism 
     _renderer = r; 
    } 

    public void PrintToConsole(List<T> list) { 
     foreach (var item in list) { 
      Console.Write(_renderer.Render(item)); 
     } 
    } 
} 

Các nhà phát triển mà cần phải sử dụng thành phần của bạn phải:

1) thực hiện các giao diện

2) vượt qua các lớp thực sự cho PrintListToConsole

class myrenderer : PrindListConsoleRender<int> { 
    public String Render(int input) { 
     return "Number: " + input; 
    } 
} 

class Program { 
    static void Main(string[] args) { 
     var list = new List<int> { 1, 2, 3 }; 
     var printer = new PrintListToConsole<int>(); 
     printer.SetRenderer(new myrenderer()); 
     printer.PrintToConsole(list); 
     string result = Console.ReadLine(); 
    } 
} 

Sử dụng Func nó đơn giản hơn nhiều

Bên trong thành phần bạn định nghĩa một tham số có kiểu Func rằng đại diện cho một giao diện của một hàm mà phải mất một tham số đầu vào của loại T và trả về một chuỗi (đầu ra cho giao diện điều khiển)

class PrintListToConsole<T> { 

    private Func<T, String> _renderFunc; 

    public void SetRenderFunc(Func<T, String> r) { 
     // this is the point where I can set the render mechanism 
     _renderFunc = r; 
    } 

    public void StampaFunc(List<T> list) { 
     foreach (var item in list) { 
      Console.Write(_renderFunc(item)); 
     } 
    } 
} 

Khi việc sử dụng nhà phát triển thành phần của bạn nó chỉ đơn giản là chuyển đến thành phần thực thi kiểu Func, đó là một hàm tạo đầu ra cho bàn điều khiển.

class Program { 
    static void Main(string[] args) { 
     var list = new Array[1, 2, 3]; 
     var printer = new PrintListToConsole<int>(); 
     printer.SetRenderFunc((o) => "Number:" + o); 
     string result = Console.ReadLine(); 
    } 
} 

Func cho phép bạn xác định giao diện phương pháp chung khi đang di chuyển. Bạn xác định loại đầu vào là gì và loại đầu ra là gì. Đơn giản và súc tích.

+1

Cảm ơn bạn đã đăng Marco này. Nó đã thực sự giúp tôi. Tôi đã cố gắng để hiểu func trong một thời gian và cũng tích cực sử dụng nó trong lập trình của tôi. Ví dụ này sẽ xóa đường dẫn. Tôi đã phải thêm phương thức StampaFunc như được bỏ qua trong mã ban đầu để ngăn nó hiển thị. –

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