Func nedir, nasıl ve ne zaman kullanılır


115

Ne Func<>için kullanılır ve ne için kullanılır?


4
Bu sadece belirli bir imzaya sahip temsilciler için bir kısayol. Aşağıdaki cevapları tam olarak anlamak için delegeleri anlamanız gerekir ;-)
Theo Lenndorff

2
@Oded cevabında diyor kiIf 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

Yanıtlar:


76

Func<T>türün bir değerini döndüren bir yöntem için önceden tanımlanmış bir temsilci türüdür T.

Başka bir deyişle, bu türü bir değer döndüren bir yönteme başvurmak için kullanabilirsiniz T. Örneğin

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

bu şekilde referans verilebilir

Func<string> f = GetMessage;

Ama aynı zamanda statik tek argüman fonksiyonunu =) temsil edebilir
Ark-kun

2
@ Ark-kun hayır, bu doğru değil. Nedir Func<T>IS delegate TResult Func<out TResult>(). Tartışma yok. Func<T1, T2>bir argüman alan bir işlev olacaktır.
Brian Rasmussen

4
Hayır, haklıyım. static int OneArgFunc(this string i) { return 42; } Func<int> f = "foo".OneArgFunc;. =)
Ark-kun

1
Bu, özel bir uzatma yöntemidir.
Brian Rasmussen

Bununla ilgili tek özel şey Extension, CLR değil, yalnızca C # / VB.Net derleyicileri tarafından okunan özniteliktir. Temel olarak, örnek yöntemlerin (statik işlevlerin aksine) gizli bir 0'ıncı "this" parametresi vardır. Dolayısıyla, 1 bağımsız değişkenli örnek yöntemi 2 bağımsız değişkenli statik işleve çok benzer. Ardından, hedef nesneyi ve işlev işaretçisini depolayan delegelerimiz var . Temsilciler ilk argümanı hedefte saklayabilir veya yapmayabilir .
Ark-kun

87

Yer tutucu olarak düşünün. Belirli bir modeli izleyen ancak herhangi bir özel işleve bağlı olması gerekmeyen bir kodunuz olduğunda oldukça yararlı olabilir.

Örneğin, Enumerable.Selectuzantı yöntemini düşünün .

  • model : a sırayla her öğe, bu madde (örneğin, bir özellik) gelen değeri seçmek ve bu değerlerin oluşan yeni bir dizi oluşturmak için.
  • Yer tutucu şudur: yukarıda açıklanan dizi için gerçekten değerleri alan bazı seçici işlevler.

Bu yöntem Func<T, TResult>, herhangi bir somut işlev yerine a alır . Bu, herhangi bir , yukarıdaki modelin geçerli olduğu bağlamda .

Örneğin, bir diyelim ki List<Person>listedeki her kişinin adını istiyorum. Bunu yapabilirim:

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

Ya da her insanın yaşını istediğimi söyle :

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

Hemen, iki farklı işleve sahip bir modeli (ve ) temsil eden aynı kodu nasıl kullanabildiğimi görebilirsiniz ( veSelectp => p.Namep => p.Age ) .

Alternatif, farklı Selectbir değer türü için bir dizi taramak istediğiniz her seferin farklı bir versiyonunu yazmaktır . Dolayısıyla, yukarıdakiyle aynı etkiyi elde etmek için şunlara ihtiyacım var:

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

Yer tutucu olarak hareket eden bir delege ile bu tür durumlarda aynı kalıbı defalarca yazmaktan kendimi kurtarıyorum.


66

Func<T1, T2, ..., Tn, Tr> (T1, T2, ..., Tn) bağımsız değişkenleri alan ve Tr döndüren bir işlevi temsil eder.

Örneğin, bir işleviniz varsa:

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

Bunu bir tür işlev değişkeni olarak kaydedebilirsiniz:

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

Ve sonra tam olarak sqr kullanacağınız gibi kullanın:

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

vb.

Yine de bunun bir temsilci olduğunu unutmayın, daha gelişmiş bilgiler için belgelere bakın.


1
Mükemmel cevap, ancak statik anahtar kelimeyi derlemek için gerekli
boctulus

16

Func<T>"Anında" kişiselleştirilmesi gereken bir bileşen oluşturduğumda çok faydalı buluyorum .

Şu çok basit örneği ele alalım: bir PrintListToConsole<T>bileşen.

Bu nesne listesini konsola yazdıran çok basit bir nesne. Onu kullanan geliştiricinin çıktıyı kişiselleştirmesine izin vermek istiyorsunuz.

Örneğin, belirli bir sayı biçimi türünü tanımlamasına izin vermek istiyorsunuz vb.

Func olmadan

İlk olarak, girdiyi alan ve konsola yazdırmak üzere dizeyi üreten bir sınıf için bir arabirim oluşturmanız gerekir.

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

Daha sonra PrintListToConsole<T>, önceden oluşturulmuş arayüzü alan ve onu listenin her bir öğesi üzerinde kullanan sınıfı oluşturmanız gerekir .

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

Bileşeninizi kullanması gereken geliştiricinin şunları yapması gerekir:

  1. arayüzü uygula

  2. gerçek sınıfı geçmek 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();   
        }   
    }

Func'u kullanmak çok daha basit

Bileşen İçerisinde tipte bir parametreyi bir fonksiyonun bir ara birim olan bir dizi (konsol için çıkış) T türü bir giriş parametresi alır ve dönerFunc<T,String>

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

Geliştirici bileşeninizi kullandığında, bileşene Func<T, String>, konsolun çıktısını oluşturan bir işlev olan türün uygulamasını basitçe aktarır .

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>anında genel bir yöntem arabirimi tanımlamanıza izin verir. Girdinin ne tür olduğunu ve çıktının ne tür olduğunu tanımlarsınız. Basit ve öz.


2
Bu Marco'yu gönderdiğiniz için teşekkürler. Bana gerçekten yardımcı oldu. Bir süredir func'u anlamaya çalışıyorum ve programlamamda da aktif olarak kullanıyorum. Bu örnek yolu temizleyecektir. Orijinal kodda görünmesini engelleyen dışarıda bırakılan StampaFunc yöntemini eklemek zorunda kaldım.
Siwoku Adeola

1
Sanırım Func örneğinde eksik bir satır var, yazdırma işlevi veya StampaFunc için çağrı nerede?
Bashar Abu Shamaa

11

Func<T1,R>ve diğer önceden tanımlanmış jenerik Funcdelegeler ( Func<T1,T2,R>, Func<T1,T2,T3,R>ve diğerleri) son jenerik parametrenin türünü döndürür jenerik delegeler bulunmaktadır.

Parametrelere bağlı olarak farklı türler döndürmesi gereken bir işleviniz varsa Func, dönüş türünü belirten bir temsilci kullanabilirsiniz .


7

Bu sadece önceden tanımlanmış genel bir temsilcidir. Bunu kullanarak her delegeyi bildirmenize gerek yoktur. Önceden tanımlanmış başka bir delege daha Action<T, T2...>vardır, bu aynıdır ancak void döndürür.


0

Belki biraz bilgi eklemek için çok geç değil.

Toplam:

Func, 0 ila 16 giriş parametresi kullanarak aynı imzaya sahip bir yöntemi (temsilcilerin yaptığı gibi) işaret etmenize olanak tanıyan ve bir şey döndürmesi gereken Sistem ad alanında tanımlanan özel bir temsilcidir.

İsimlendirme ve nasıl kullanılır:

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

Tanım:

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

Nerede kullanılır:

Lambda ifadelerinde ve anonim yöntemlerde kullanılır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.