Apa itu Func, bagaimana dan kapan digunakan

115

Untuk apa Func<>dan digunakan untuk apa?

belajar
sumber
4
Ini hanya jalan pintas untuk delegasi dengan tanda tangan tertentu. Untuk memahami sepenuhnya jawaban di bawah, Anda harus memahami para delegasi ;-)
Theo Lenndorff
2
Dalam jawaban dari @Oded dikatakanIf 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

Jawaban:

76

Func<T>adalah tipe delegasi yang telah ditentukan sebelumnya untuk metode yang mengembalikan beberapa nilai tipe T.

Dengan kata lain, Anda bisa menggunakan tipe ini untuk mereferensikan metode yang mengembalikan beberapa nilai T. Misalnya

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

mungkin direferensikan seperti ini

Func<string> f = GetMessage;
Brian Rasmussen
sumber
Tapi itu juga bisa mewakili fungsi satu argumen statis =)
Ark-kun
2
@ Ark-kun tidak, itu tidak benar. Definisi dari Func<T>adalah delegate TResult Func<out TResult>(). Tidak ada argumen. Func<T1, T2>akan menjadi fungsi yang membutuhkan satu argumen.
Brian Rasmussen
4
Tidak, saya benar. static int OneArgFunc(this string i) { return 42; } Func<int> f = "foo".OneArgFunc;. =)
Ark-kun
1
Itu adalah metode penyuluhan yang istimewa.
Brian Rasmussen
Satu-satunya hal khusus tentang itu adalah Extensionatribut yang hanya dibaca oleh kompiler C # / VB.Net, bukan CLR. Pada dasarnya, metode instance (tidak seperti fungsi statis) memiliki parameter "ini" ke-0 yang tersembunyi. Jadi, metode instance 1-argumen sangat mirip dengan fungsi statis 2-argumen. Kemudian, kami memiliki delegasi yang menyimpan objek target dan penunjuk fungsi . Delegasi dapat menyimpan argumen pertama dalam target atau tidak melakukan itu.
Ark-kun
87

Anggap saja sebagai placeholder. Ini bisa sangat berguna ketika Anda memiliki kode yang mengikuti pola tertentu tetapi tidak perlu terikat pada fungsi tertentu.

Misalnya, pertimbangkan Enumerable.Selectmetode ekstensi.

  • The Pola adalah: untuk setiap item secara berurutan, pilih beberapa nilai dari item yang (misalnya, properti) dan membuat urutan baru yang terdiri dari nilai-nilai ini.
  • The placeholder adalah: beberapa fungsi pemilih yang benar-benar mendapat nilai-nilai untuk urutan yang dijelaskan di atas.

Metode ini mengambil Func<T, TResult>alih fungsi konkret. Ini memungkinkannya untuk digunakan dalam konteks apa pun di mana pola di atas berlaku.

Jadi misalnya, katakan saya punya a List<Person>dan saya hanya ingin nama setiap orang yang ada di daftar. Aku bisa melakukan ini:

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

Atau katakan saya ingin usia setiap orang:

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

Langsung saja, Anda dapat melihat bagaimana saya dapat memanfaatkan kode yang sama yang mewakili pola (dengan Select) dengan dua perbedaan fungsi ( p => p.Namedanp => p.Age ).

Alternatifnya adalah menulis versi berbeda Selectsetiap kali Anda ingin memindai urutan untuk jenis nilai yang berbeda. Jadi untuk mencapai efek yang sama seperti di atas, saya membutuhkan:

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

Dengan seorang delegasi bertindak sebagai placeholder, saya membebaskan diri saya dari keharusan menulis pola yang sama berulang kali dalam kasus seperti ini.

Dan Tao
sumber
66

Func<T1, T2, ..., Tn, Tr> mewakili sebuah fungsi, yang mengambil argumen (T1, T2, ..., Tn) dan mengembalikan Tr.

Misalnya, jika Anda memiliki fungsi:

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

Anda bisa menyimpannya sebagai semacam variabel fungsi:

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

Dan kemudian gunakan persis seperti yang Anda gunakan sqr:

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

dll.

Ingat juga, itu adalah delegasi, untuk info lebih lanjut lihat dokumentasi.

Grozz
sumber
1
Jawaban yang bagus, tetapi untuk menyusun kata kunci statis
diperlukan
16

Saya merasa Func<T>sangat berguna ketika saya membuat komponen yang perlu dipersonalisasi "dengan cepat".

Ambil contoh yang sangat sederhana ini: sebuah PrintListToConsole<T>komponen.

Objek yang sangat sederhana yang mencetak daftar objek ini ke konsol. Anda ingin mengizinkan pengembang yang menggunakannya mempersonalisasi keluaran.

Misalnya, Anda ingin membiarkan dia menentukan jenis format angka tertentu dan seterusnya.

Tanpa Func

Pertama, Anda harus membuat antarmuka untuk kelas yang menerima input dan menghasilkan string untuk dicetak ke konsol.

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

Kemudian Anda harus membuat kelas PrintListToConsole<T>yang mengambil antarmuka yang dibuat sebelumnya dan menggunakannya di setiap elemen daftar.

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

Pengembang yang perlu menggunakan komponen Anda harus:

  1. menerapkan antarmuka

  2. meneruskan kelas nyata ke 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();   
        }   
    }

Menggunakan Func jauh lebih sederhana

Di dalam komponen Anda menentukan parameter tipe Func<T,String>yang mewakili antarmuka fungsi yang mengambil parameter input tipe T dan mengembalikan string (output untuk konsol)

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

Ketika pengembang menggunakan komponen Anda, dia cukup meneruskan implementasi Func<T, String>tipe ke komponen , yaitu fungsi yang membuat keluaran untuk konsol.

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>memungkinkan Anda menentukan antarmuka metode umum dengan cepat. Anda menentukan jenis masukan dan jenis keluarannya. Sederhana dan ringkas.

Marco Staffoli
sumber
2
Terima kasih telah memposting Marco ini. Itu sangat membantu saya. Saya telah mencoba untuk memahami func untuk sementara waktu dan juga secara aktif menggunakannya dalam pemrograman saya. Contoh ini akan menghapus jalur. Saya harus menambahkan metode StampaFunc seperti yang ditinggalkan dalam kode asli yang mencegahnya untuk ditampilkan.
Siwoku Adeola
1
Saya rasa ada baris yang terlewat dalam sampel Func, Di mana panggilan untuk fungsi cetak atau StampaFunc?
Bashar Abu Shamaa
11

Func<T1,R>dan Funcdelegasi generik standar lainnya ( Func<T1,T2,R>, Func<T1,T2,T3,R>dan lainnya) adalah delegasi generik yang mengembalikan tipe parameter generik terakhir.

Jika Anda memiliki fungsi yang perlu mengembalikan tipe berbeda, bergantung pada parameternya, Anda bisa menggunakan Funcdelegasi, menentukan tipe kembalian.

Oded
sumber
7

Ini hanyalah delegasi umum yang telah ditentukan sebelumnya. Dengan menggunakannya, Anda tidak perlu mendeklarasikan setiap delegasi. Ada delegasi lain yang ditentukan sebelumnya Action<T, T2...>, yang sama tetapi mengembalikan kosong.

Stefan Steinegger
sumber
0

Mungkin belum terlambat untuk menambahkan beberapa info.

Jumlah:

Func adalah delegasi khusus yang ditentukan dalam namespace Sistem yang memungkinkan Anda untuk menunjuk ke metode dengan tanda tangan yang sama (seperti yang dilakukan delegasi), menggunakan 0 hingga 16 parameter input dan itu harus mengembalikan sesuatu.

Nomenklatur & how2use:

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

Definisi:

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

Dimana itu digunakan:

Ini digunakan dalam ekspresi lambda dan metode anonim.

overRideKode
sumber