Menu

C# Func ve Action: Generic Delegeler, Predicate ve Örnekler

C#'ta Func ve Action nasıl çalışır: değer döndüren fonksiyonlar için Func<T, TResult>, döndürmeyenler için Action<T>, Predicate ve Comparison, fonksiyon geçirmek ve döndürmek, bir komut dictionary'si, async lambda'lar ve bunun yerine ne zaman kendi delegate'inizi bildirmeniz gerektiği.

Bu sayfada çalıştırılabilir editörler var - düzenle, çalıştır ve sonucu anında gör.

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

TipParametrelerDöndürür
Actionyokvoid
Action<T>Tvoid
Action<T1, T2> ... 16'ya kadarT1, T2, ...void
Func<TResult>yokTResult
Func<T, TResult>TTResult
Func<T1, T2, TResult> ... 16'ya kadarT1, T2, ...TResult
Predicate<T>Tbool
Comparison<T>T, Tint (negatif, sıfır, pozitif)
Converter<TIn, TOut>TInTOut

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, out ya da in parametreleri var. Func bunları ifade edemez, bu yüzden TryParse tarzı bir fonksiyon delegate 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 params parametresine 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);.

Coddy programming languages illustration

Coddy ile kodlamayı öğren

BAŞLA