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


115

Nó là Func<>gì và nó được sử dụng để làm gì?


4
Nó chỉ là một phím tắt cho các đại biểu với một chữ ký cụ thể. Để hiểu đầy đủ câu trả lời bên dưới, bạn sẽ cần phải hiểu đại biểu ;-)
Theo Lenndorff

2
Trong câu trả lời của @Oded nó nóiIf you have a function that needs to return different types, depending on the parameters, you can use a Func delegate, specifying the return type.
LCJ

Câu trả lời:


76

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

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

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

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

Func<string> f = GetMessage;

Nhưng nó cũng có thể đại diện cho một chức năng tĩnh một lập luận =)
Ark-kun

2
@ Ark-kun không, điều đó không chính xác. Định nghĩa của Func<T>delegate TResult Func<out TResult>(). Không có đối số. Func<T1, T2>sẽ là một hàm nhận một đối số.
Brian Rasmussen

4
Không, tôi đúng. static int OneArgFunc(this string i) { return 42; } Func<int> f = "foo".OneArgFunc;. =)
Ark-kun

1
Đó là một phương pháp mở rộng đặc biệt.
Brian Rasmussen

Điều đặc biệt duy nhất về nó là Extensionthuộc tính chỉ được đọc bởi trình biên dịch C # / VB.Net, không phải CLR. Về cơ bản, các phương thức thể hiện (không giống như các hàm tĩnh) có tham số "this" thứ 0 ẩn. Vì vậy, phương thức thể hiện 1 đối số rất giống với hàm tĩnh 2 đối số. Sau đó, chúng ta có các đại biểu lưu trữ đối tượng đíchcon trỏ hàm . Các đại biểu có thể lưu trữ đối số đầu tiên trong target hoặc không làm điều đó.
Ark-kun

87

Hãy coi nó như một trình giữ chỗ. Nó có thể khá hữu ích khi bạn có mã tuân theo một mẫu nhất định nhưng không cần ràng buộc với bất kỳ chức năng cụ thể nào.

Ví dụ, hãy xem xét Enumerable.Selectphương pháp mở rộng.

  • Các mô hình 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 bộ chọn thực sự nhận các giá trị cho chuỗi được mô tả ở trên.

Phương pháp này Func<T, TResult>thay vì bất kỳ hàm 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 mà mẫu trên được áp dụng.

Vì vậy, ví dụ, giả sử tôi có 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 mỗi người:

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

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

Cách thay thế sẽ là viết một phiên bản khác Selectmỗi khi bạn muốn quét một chuỗi để tìm một loại giá trị khác. Vì vậy, để đạt được hiệu quả tương tự như trên, tôi sẽ 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 ủy nhiệm đóng vai trò là trình giữ chỗ, tôi không cần phải viết đi viết lại cùng một mẫu trong những trường hợp như thế này.


66

Func<T1, T2, ..., Tn, Tr> đại diện cho một hàm, nhận (T1, T2, ..., Tn) đối số 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ó dưới dạng một số loại biến hàm:

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)));

Vân vân.

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


1
Câu trả lời xuất sắc, nhưng đối với compilling tĩnh từ khóa được neeed
boctulus

16

Tôi thấy Func<T>rất hữu ích khi tôi tạo một thành phần cần được cá nhân hóa "một cách nhanh chóng".

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

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

Ví dụ: bạn muốn để anh ta xác định một loại định dạng số cụ thể, v.v.

Không có chức năng

Đầu tiên, bạn phải tạo giao diện cho một lớp nhận đầu vào và tạo chuỗi để in ra bảng điều khiển.

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

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

class PrintListToConsole<T> {

    private PrintListConsoleRender<T> _renderer;

    public void SetRenderer(PrintListConsoleRender<T> r) {
        // this is the point 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));
        }
    }   
}

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

  1. triển khai giao diện

  2. vượt qua lớp học thực sự cho PrintListToConsole

    class MyRenderer : PrintListConsoleRender<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 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<T,String>đó đạ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 Print(List<T> list) {
        foreach (var item in list) {
            Console.Write(_renderFunc(item));
        }
    }
}

Khi nhà phát triển sử dụng thành phần của bạn, anh ta chỉ cần chuyển cho thành phần việc triển khai Func<T, String>kiểu, đó là một hàm tạo đầu ra cho bảng điều khiển.

class Program {
    static void Main(string[] args) {
        var list = new List<int> { 1, 2, 3 }; // should be a list as the method signature expects
        var printer = new PrintListToConsole<int>();
        printer.SetRenderFunc((o) => "Number:" + o);
        printer.Print(list); 
        string result = Console.ReadLine();
    }
}

Func<T>cho phép bạn xác định một giao diện phương pháp chung một cách nhanh chóng. Bạn xác định loại đầu vào là gì và loại đầu ra là gì. Đơn giản và ngắn gọn.


2
Cảm ơn vì đã đă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 mình. Ví dụ này sẽ xóa đường dẫn. Tôi đã phải thêm phương thức StampaFunc vì phương thức đó đã bị bỏ sót trong mã gốc khiến nó không hiển thị.
Siwoku Adeola

1
Tôi nghĩ rằng có một dòng bị thiếu trong mẫu Func, Đâu là lời gọi hàm in hoặc StampaFunc?
Bashar Abu Shamaa

11

Func<T1,R>và người kia được xác định trước chung Funccác đại biểu ( 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ó một hàm cần trả về các kiểu khác nhau, tùy thuộc vào các tham số, bạn có thể sử dụng một Funcủy nhiệm, chỉ định kiểu trả về.


7

Nó chỉ là một đại biểu chung được xác định trước. Sử dụng nó, bạn không cần phải 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...>cũng giống như vậy nhưng trả về giá trị vô hiệu.


0

Có lẽ vẫn chưa muộn để thêm một số thông tin.

Tổng:

Func là một đại biểu tùy chỉnh được xác định trong không gian tên Hệ thống cho phép bạn trỏ đến một phương thức có cùng chữ ký (như các đại biểu), sử dụng 0 đến 16 tham số đầu vào và điều đó phải trả về một cái gì đó.

Danh pháp & cách sử dụng:

Func<input_1, input_2, ..., input1_6, output> funcDelegate = someMethod;

Định nghĩa:

public delegate TResult Func<in T, out TResult>(T arg);

Nơi nó được sử dụng:

Nó được sử dụng trong các biểu thức lambda và các phương thức ẩn danh.

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.