Func ve Action, .NET'in sizin için bildirdiği generic delegate tipleridir; böylece önce bir delegate tipi bildirmeden fonksiyonları saklayıp geçirebilirsiniz. Func bir değer döndürür; Action hiçbir şey döndürmez.
Çıktı:
4
46.00
False
Hello!
Mia scored 87
Hatırlanacak kural: bir Func'ta son tip argümanı dönüş tipidir. Func<int, int, bool> iki int alır ve bir bool döndürür. Func<int> hiçbir şey almaz ve bir int döndürür. Bir Action'ın tip argümanlarının hepsi parametredir.
Aile
| Tip | Parametreler | Döndürür |
|---|---|---|
Action | yok | void |
Action<T> | T | void |
Action<T1, T2> ... 16'ya kadar | T1, T2, ... | void |
Func<TResult> | yok | TResult |
Func<T, TResult> | T | TResult |
Func<T1, T2, TResult> ... 16'ya kadar | T1, T2, ... | TResult |
Predicate<T> | T | bool |
Comparison<T> | T, T | int (negatif, sıfır, pozitif) |
Converter<TIn, TOut> | TIn | TOut |
Predicate, Comparison ve Converter daha eskidir ve List<T> ile Array metotlarında görünür: FindAll(Predicate<T>), Sort(Comparison<T>), ConvertAll(Converter<T, TOut>). LINQ her yerde Func kullanır; list.Where(...) ile list.FindAll(...)'ın aynı şekilde ama farklı tiplerde lambda almasının nedeni budur:
Çıktı:
17, 15
34, 52, 28
52, 34, 28, 17, 15
52y 34y 28y 17y 15y
Metotlara Fonksiyon Geçirmek
Bir Func ya da Action parametresi, algoritmanın değişen kısmını çağıranın sağlamasına izin verir. İki yaygın şekil "bunu çalıştır ve ölç" ile "işe yarayana kadar bunu dene"dir:
Çıktı:
attempt 1 failed
attempt 2 failed
<html>ok</html>
clicks: 2
Retry generic'tir, bu yüzden Func<int, T>'si her şeyi döndürebilir; derleyici T'yi lambda'dan string olarak çıkardı. Twice'a geçirilen lambda yerel clicks'i değiştirir; bu, bir lambda'nın değerinin bir kopyasını değil değişkenin kendisini yakaladığı için çalışır.
Metot Grupları: Bir Metodu Adıyla Geçirmek
Bir Func ya da Action'ı doldurmak için lambda'ya ihtiyacınız yoktur. İmzası uyan mevcut bir metodun adı (bir "metot grubu") doğrudan dönüşür:
Çıktı:
parsed: 43
sat is weekend: True
total quantity: 20
Select(int.Parse), bir çağrı katmanı eksiğiyle Select(s => int.Parse(s)) ile aynıdır. Derleyici hedef tipe uyan overload'u seçer: int.Parse'ın birkaç overload'u vardır ve yalnızca Parse(string) Func<string, int> ile eşleşir. Derleyici birine karar veremediğinde (birkaç overload eşit derecede uyduğunda ya da generic bir metodun tip argümanları bir metot grubundan çıkarılamadığında) çağrı derlenmez ve çağrıyı açık hale getiren bir lambda sorunu çözer.
Bir Func olarak kullanılan bir metot grubu da, tıpkı bir lambda gibi, delegate her çağrıldığında sonradan çalışır. Fark yalnızca nasıl yazıldığındadır.
Fonksiyon Döndürmek
Bir metot bir fonksiyon oluşturup döndürebilir. Döndürülen Func, oluşturulduğu değerleri taşır:
Çıktı:
140
180
162
Twice(staff), personel indirimini iki kez uygulayan yeni bir fonksiyon oluşturur: 200, sonra 180, sonra 162.
Fonksiyonlardan Oluşan Bir Dictionary
Fonksiyonları bir dictionary'de saklamak uzun bir switch'i bir arama tablosuna çevirir. Komut işleyicileri, hesap makineleri ve menü eylemleri için olağan şekil budur ve çalışma zamanında yeni girdiler eklenebilir:
Çıktı:
12 + 30 = 42
7 * 6 = 42
9 / 0 = cannot divide by zero
2 ^ 3 = unknown operator
Hello, Sam
DONE
Bir operatör eklemek, döngüde hiçbir değişiklik olmadan bir dictionary girdisi daha demektir. İkinci dictionary'ye geçirilen StringComparer.OrdinalIgnoreCase komut adlarını harf duyarsız yapar.
Async Fonksiyonlar: Action Değil Func<Task>
async kodda delegate tipi, çağıranın işi bekleyip bekleyemeyeceğine karar verir:
Func<Task> save = async () => await File.WriteAllTextAsync("a.txt", "data");
await save(); // the caller can await it and see its exceptions
Func<int, Task<string>> load = async id => await FetchUserAsync(id);
string user = await load(42);
Action bad = async () => await File.WriteAllTextAsync("a.txt", "data");
bad(); // async void: fire and forget, exceptions escape
Bir Action'a atanan async bir lambda bir async void metodu olur: hiçbir şey onu await edemez ve içindeki bir istisna asla çağırana ulaşmaz. Thread pool'da (ya da bir masaüstü uygulamasında UI thread'inde) yeniden fırlatılır ve orada genellikle süreci çökertir. Async iş kabul eden bir metot yazdığınızda bir Func<Task> (ya da Func<Task<T>>) alın.
Kendi Delegate'inizi Ne Zaman Bildirmelisiniz
Func ve Action çoğu ihtiyacı karşılar. Şu durumlarda adlandırılmış bir delegate tipi bildirin:
- İmzada
ref,outya dainparametreleri var.Funcbunları ifade edemez, bu yüzdenTryParsetarzı bir fonksiyondelegate bool TryParser<T>(string text, out T value);gerektirir. - Ad, public bir API'de bir rolü belgeliyor. Bir metot imzasındaki
delegate bool Validator(string input),Func<string, bool>'dan daha fazlasını söyler ve parametre adları IntelliSense'te görünür. - Bir
paramsparametresine ihtiyacınız var; generic delegeler bunu da içeremez.
Özel delegate tiplerinin hâlâ göründüğü diğer yer event'lerdir, ama yerleşik EventHandler<TEventArgs> çoğunu karşılar. Delegeler sayfası delegate tiplerini bildirmeyi ve birleştirmeyi ele alır.
Sıkça Sorulan Sorular
C#'ta Func ile Action arasındaki fark nedir?
Func bir değer döndürür, Action döndürmez. Func<int, string, bool>'da son tip argümanı (bool) dönüş tipidir, diğerleri parametrelerdir. Action<int, string> bir int ve bir string alır ve void döndürür. İkisinin de 0 ile 16 parametreli sürümleri vardır.
C#'ta bir fonksiyon parametre olarak nasıl geçirilir?
Parametreyi doğru şekilde bir Func ya da Action olarak bildirin ve metodun içinde çağırın: static decimal Apply(decimal price, Func<decimal, decimal> rule) => rule(price);. Çağıranlar bir lambda (Apply(80m, p => p * 0.9m)) ya da bir metot adı (Apply(80m, HalfPrice)) geçirir.
C#'ta Predicate<T> nedir?
Bir T alıp bool döndüren, Func<T, bool> ile aynı şekle sahip bir delegate tipidir. Func'tan daha eskidir ve List<T>.Find, FindAll, RemoveAll, Exists ve Array.Find tarafından kullanılır. İki tip birbirine dönüşmez, ama bir lambda ikisine de dönüşür.
Func async kodla nasıl kullanılır?
Sonucu olmayan async bir fonksiyon için Func<Task>, sonucu olan için Func<T, Task<TResult>> kullanın; böylece çağıran onu await edebilir. Async bir lambda'yı bir Action'a atamaktan kaçının: bu onu async void yapar; await edilemez ve istisnaları çağıran tarafından yakalanamaz.
Func'ın out ya da ref parametreleri olabilir mi?
Hayır. Func ve Action'ın generic parametreleri sıradan değer parametreleridir, bu yüzden Func<string, out int, bool> yazmanın bir yolu yoktur. TryParse gibi bir imza için özel bir delegate bildirin: delegate bool TryParser<T>(string text, out T value);.