Tahmin <T> yerine neden Func <T, bool>?


210

Bu sadece birisinin iyi bir cevabı olup olmadığını merak ettiğim bir merak sorusu:

.NET Framework Sınıf Kitaplığı'nda, örneğin şu iki yönteme sahibiz:

public static IQueryable<TSource> Where<TSource>(
    this IQueryable<TSource> source,
    Expression<Func<TSource, bool>> predicate
)

public static IEnumerable<TSource> Where<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, bool> predicate
)

Neden Func<TSource, bool>yerine kullanıyorlar Predicate<TSource>? Görünüşe Predicate<TSource>sadece tarafından kullanılır List<T>ve Array<T>süre, Func<TSource, bool>hemen hemen herkes tarafından kullanılan Queryableve Enumerableyöntemler ve uzatma yöntemleri bununla ne haber ...?


21
Ah evet, bunların tutarsız kullanımı beni de delirtiyor.
George Mauer

Yanıtlar:


170

İken Predicateaynı anda tanıtılan edildiğini List<T>ve Array<T>, .net 2.0, farklı Funcve Actionvaryantları .net 3.5 geliyor.

Dolayısıyla bu Functahminler esas olarak LINQ operatörlerinde tutarlılık için kullanılır. Kullanmayla ilgili .net 3.5'e itibariyle Func<T>ve kılavuz devletler :Action<T>

Yeni LINQ türlerini kullanıyor musunuz Func<>ve Expression<>yerine özel delege ve yüklemler


7
Serin,) = önce bu guildelines hiç görülmemiş
Svish

6
En çok oyu olduğu için bunu cevap olarak kabul edecek. ve Jon Skeet'in çok sayıda temsilcisi olduğu için: p
Svish

4
Yazıldığı gibi tuhaf bir rehber. Şüphesiz "Yeni LINQ tiplerini kullan" işlevini <Func <> "ve" Action <> "[...]" belirtmelidir. İfade <> tamamen farklı bir şeydir.
Jon Skeet

3
Aslında, bunu bir LINQ sağlayıcısı yazmakla ilgili olan kılavuzun bağlamına yerleştirmelisiniz. Yani evet, LINQ operatörleri için kılavuz Func <> ve Expression <> parametrelerini uzatma yöntemleri için parametre olarak kullanmaktır. Katılıyorum Func ve Eylem
Jb Evain

Bence Skeet'in ifadesi <> İfadesi'nin bir rehber olmadığıdır. Bu türü tanımak ve onunla farklı bir şey yapmak bir C # derleyici özelliğidir.
Daniel Earwicker

116

Bunu daha önce merak ettim. Predicate<T>Temsilciyi seviyorum - güzel ve açıklayıcı. Bununla birlikte, aşağıdakilerin aşırı yüklenmelerini dikkate almanız gerekir Where:

Where<T>(IEnumerable<T>, Func<T, bool>)
Where<T>(IEnumerable<T>, Func<T, int, bool>)

Bu, girişin dizinine göre de filtrelemenizi sağlar. Bu güzel ve tutarlı, oysa:

Where<T>(IEnumerable<T>, Predicate<T>)
Where<T>(IEnumerable<T>, Func<T, int, bool>)

olmaz.


11
<İnt, bool> tahmin etmek biraz çirkin olur - bir yüklem genellikle tek bir değere dayanır (bilgisayar biliminin IME'si). Tahmin edilebilir <Pair <T, int >> elbette, ama bu daha çirkin :)
Jon Skeet

çok doğru ... hehe. Hayır, sanırım Func daha temiz.
Svish

2
Yönergeler nedeniyle diğer yanıtı kabul etti. Gerçi birden fazla cevabı işaretleyebilsem! Bir dizi cevabı "Çok dikkat çekici" veya "Ayrıca cevaba ek olarak belirtilmesi gereken iyi bir noktaya sahip" olarak işaretleyebilseydim iyi olur
Svish

6
Hm, sadece ilk yorumu yanlış yazdığımı gördüm: P benim önerim elbette Predicate <T, int> ...
Svish

4
@JonSkeet Bu sorunun eski olduğunu biliyorum, ama ironik olan ne biliyor musun? Parametrenin adı Whereuzantısı yöntemi olup predicate. Heh = S.
Conrad Clark

32

Kesinlikle Funcbelirli bir delege kullanmak için asıl neden , C #'ın ayrı ayrı beyan edilen delegelere tamamen farklı türler olarak davranmasıdır.

Olsa Func<int, bool>ve Predicate<int>her ikisi aynı argüman ve dönüş türleri vardır, bunlar atama-uyumlu değildir. Dolayısıyla, her kütüphane her delege kalıbı için kendi temsilci türünü bildirirse, kullanıcı dönüşümleri gerçekleştirmek için "köprüleme" delegeleri eklemediği sürece bu kütüphaneler birlikte çalışamaz.

    // declare two delegate types, completely identical but different names:
    public delegate void ExceptionHandler1(Exception x);
    public delegate void ExceptionHandler2(Exception x);

    // a method that is compatible with either of them:
    public static void MyExceptionHandler(Exception x)
    {
        Console.WriteLine(x.Message);
    }

    static void Main(string[] args)
    {
        // can assign any method having the right pattern
        ExceptionHandler1 x1 = MyExceptionHandler; 

        // and yet cannot assign a delegate with identical declaration!
        ExceptionHandler2 x2 = x1; // error at compile time
    }

Microsoft, herkesi Func'u kullanmaya teşvik ederek bunun uyumsuz delege türleri sorununu hafifleteceğini umuyor. Herkesin delegeleri güzelce birlikte oynayacaklar, çünkü sadece parametre / dönüş türlerine göre eşleştirilecekler.

Tüm problemleri çözmez, çünkü Func(ve Action) sahip olamaz outveya refparametreler olamaz , ancak bunlar daha az kullanılır.

Güncelleme: yorumlarda Svish diyor:

Yine de, Func'tan Predicate ve Back'e bir parametre türünü değiştirmek herhangi bir fark yaratmıyor mu? En azından hala sorunsuz derliyor.

Evet, programınız Mainişlevimin ilk satırında olduğu gibi yalnızca delegelere yöntem atarsa . Derleyici, yönteme ileten yeni bir temsilci nesnesine sessizce kod üretir. Yani benim Mainfonksiyonumda, bir soruna neden olmadan x1türünü değiştirebilirim ExceptionHandler2.

Ancak, ikinci satırda ilk delege başka bir delege atamaya çalışıyorum. 2. delege türünün tamamen aynı parametre ve dönüş türlerine sahip olduğu düşünüldüğünde bile, derleyici hata verirCS0029: Cannot implicitly convert type 'ExceptionHandler1' to 'ExceptionHandler2' .

Belki bu daha açık hale getirecektir:

public static bool IsNegative(int x)
{
    return x < 0;
}

static void Main(string[] args)
{
    Predicate<int> p = IsNegative;
    Func<int, bool> f = IsNegative;

    p = f; // Not allowed
}

Benim yöntemim ve değişkenlere IsNegativeatamak için mükemmel bir şey . Ama sonra bu değişkenlerden birini diğerine atayamam.pf


Yine de, Func <T, bool> 'dan Predicate <T> ve back' e bir parametre türünü değiştirmek hiç bir fark yaratmıyor mu? En azından hala sorunsuz derliyor.
Svish

MS, geliştiricilerin bunların aynı olduğunu düşünmelerini engellemeye çalışıyor gibi görünüyor. Nedenini merak ediyorum?
Matt Kocaj

1
Parametre türünü değiştirmek, kendisine ilettiğiniz ifade yöntem çağrısına ayrı olarak tanımlandıysa bir fark yaratır, çünkü o zaman derleyici tarafından çıkartılan türden ziyade ya Func<T, bool>da Predicate<T>yerine yazılır .
Adam Ralph

30

Tavsiye (3.5 ve üzeri) Action<...>ve Func<...>- 'neden?' - bir avantajı "Predicate<T> " yalnızca "yüklem" in ne anlama geldiğini biliyorsanız anlamlı olmasıdır - aksi takdirde imza bulmak için nesne tarayıcıya (vb.) bakmanız gerekir.

Tersine Func<T,bool>standart bir patern izler; Hemen bunun bir işlev alan Tvebool - herhangi bir terminolojiyi anlamanız gerekmez - sadece doğruluk testimi uygulayın.

"Yüklem" için bu Tamam olabilirdi, ama ben standartlaştırma girişimi için teşekkür ederiz. Ayrıca, o alandaki ilgili yöntemlerle çok sayıda pariteye izin verir.

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.