2012-06-01 22 views
6

'a & amp; birlikte ihtiyaç duyarsanız, en fazla 5 Func'ı aynı IEnumerable'a uygulamak için iyi bir yol bulmaya çalışıyorum. Ben ancak, aslında sadece sonuncusu (DepartmentFilter) uygulayarak oluyor o sonuçlarından görebilirsiniz, bir kümülatif şekilde bu başvuruda düşünüyorduBelirsiz sayıda Func <TEntity, bool>

private Func<SurveyUserView,bool> _getFilterLambda(IDictionary<string, string> filters) 
{ 
    Func<SurveyUserView, bool> invokeList = delegate(SurveyUserView surveyUserView) 
    { 
     return surveyUserView.deleted != "deleted"; 
    }; 

    if (filters.ContainsKey("RegionFilter")) 
    { 
     invokeList += delegate(SurveyUserView surveyUserView) 
     { 
      return surveyUserView.Region == filters["RegionFilter"]; 
     }; 
    } 

    if (filters.ContainsKey("LanguageFilter")) 
    { 
     invokeList += delegate(SurveyUserView surveyUserView) 
     { 
      return surveyUserView.Locale == filters["LanguageFilter"]; 
     }; 
    } 

    if (filters.ContainsKey("StatusFilter")) 
    { 
     invokeList += delegate(SurveyUserView surveyUserView) 
     { 
      return surveyUserView.Status == filters["StatusFilter"]; 
     }; 
    } 

    if (filters.ContainsKey("DepartmentFilter")) 
    { 
     invokeList += delegate(SurveyUserView surveyUserView) 
     { 
      return surveyUserView.department == filters["DepartmentFilter"]; 
     }; 
    } 

    return invokeList; 
} 

: İşte ile geldi budur.

2^4 olası kombinasyon var, bu yüzden/elses çalışmayacaksa kaba kuvvet. (Sadece belirli bir lambda'yı sadece ilgili anahtar sözlüğünde bulunduğunda kullanmak istiyorum.)

DÜZENLEME: DÜZENLENEN çözümdür, ancak değerlendirildiğinde StackOverflowException neden olur. Nedenini gören var mı?

private Func<SurveyUserView,bool> _getFilterLambda(IDictionary<string, string> filters) 
    { 

     Func<SurveyUserView, bool> resultFilter = (suv) => suv.deleted != "deleted";               

     if (filters.ContainsKey("RegionFilter")) 
     { 
      Func<SurveyUserView, bool> newFilter = 
       (suv) => resultFilter(suv) && suv.Region == filters["RegionFilter"]; 
      resultFilter = newFilter; 
     } 

     if (filters.ContainsKey("LanguageFilter")) 
     { 
      Func<SurveyUserView, bool> newFilter = 
       (suv) => resultFilter(suv) && suv.Locale == filters["LanguageFilter"]; 
      resultFilter = newFilter; 
     } 

     if (filters.ContainsKey("StatusFilter")) 
     { 
      Func<SurveyUserView, bool> newFilter = 
       (suv) => resultFilter(suv) && suv.Status == filters["StatusFilter"]; 
      resultFilter = newFilter; 
     } 

     if (filters.ContainsKey("DepartmentFilter")) 
     { 
      Func<SurveyUserView, bool> newFilter = 
       (suv) => resultFilter(suv) && suv.department == filters["DepartmentFilter"]; 
      resultFilter = newFilter; 
     } 

     return resultFilter; 
    } 

DÜZENLEME: İşte bu arkadaştan bir StackOverflowException sonuçlandı ve sonsuz özyinelemeler neden oluştuğu anlamak açısından Chris Flather-

önemli şey mentorluk neden çok güzel bir açıklama anlamak olduğunu olduğunda sembolleri içinde Bir lambda çözülür (örneğin çalışma zamanında ve tanımda değil).

Bu basitleştirilmiş bir örneği ele alalım: Aynı şekilde bu tanımlamadaki statik olarak çözümlenmiş olsaydı

Func<int, int> demo = (x) => x * 2; 
Func<int, int> demo2 = (y) => demo(y) + 1; 
demo = demo2; 
int count = demo(1); 

bu çalışacak ve olmayacak:

Func<int, int> demo2 = (y) => (y * 2) + 1; 
Int count = demo2(1); 

Ama aslında anlamaya çalışmaz neyi demo2'de bulunan demo, çalışma zamanına kadar çalışır - demo2, demo olarak yeniden tanımlanır. Esasen kod artık okur:

Func<int, int> demo2 = (y) => demo2(y) + 1; 
Int count = demo2(1); 

cevap

4

Bunun yerine, VE koşulu ile mevcut birini kullanmak yeni delege inşa edebileceğini delegelere bu şekilde birleştirmek için çalışmakla:

Func<SurveyUserView, bool> resultFilter = (suv) => true; 

if (filters.ContainsKey("RegionFilter")) 
{ 
    var tmpFilter = resultFilter; 
    // Create a new Func based on the old + new condition 
    resultFilter = (suv) => tmpFilter(suv) && suv.Region == filters["RegionFilter"]; 
} 

if (filters.ContainsKey("LanguageFilter")) 
{ 
    // Same as above... 

//... Continue, then: 

return resultFilter; 

, bunu söyleniyor Orijinal IQueryable<SurveyUserView> veya IEnumerable<SurveyUserView>'u bu yönteme geçmek daha kolay olabilir ve yalnızca filtrelemek için .Where cümlelerini ekleyin. Son sorguyu yürütmeden, filtreler eklendikten sonra geri gönderebilirsiniz.

+1

UYARI! Bu, onu uygularken yığın taşması özel durumuna neden oldu. (Bu bir çeşit ironiktir.)) Reed'in çözümü mantıklı bir mantıklıdır, ancak altta yaptığı yorum (cümlelerin nerede olduğu) zincirinin buraya gitmenin tek yolu gibi görünüyor. –

+0

@TreyCarroll Bunu yeni lambda için geçici bir değişken kullanarak yukarıdakiyle aynı şekilde uyguladınız mı? –

+0

Evet. Deseni tam olarak takip ettim. Sadece bu SOE atıyor yeniden onayladı. –

2

Ben Where(...) uzantısını kullanarak ne, muhtemelen, bir IQueryable<SurveyUserView> ve IQueryable<SurveyUserView> yerine Func<...> a dönmek düşünürdüm:

// Assuming `q` is a `IQueryable<SurveyUserView>` 

if(filters.ContainsKeys["Whatever"]) 
{ 
    q = q.Where(suv => suv.Status == filters["Whatever"]; 
} 

And ing örtülü olduğunu.

+0

"Ve" uzantısı nedir? Bunu mu demek istediniz? –

+0

Ben yanlış yazdım. Onu düzenleyeceğim. 'IQueryable's değil 'İfade' şeylerini düşünüyordum. – bluevector

+0

+1 zinciri 'Where's - çok kolay. –

2
private Func<SurveyUserView, bool> _getFilterLabda(IDictionary<string, string> filters) 
    { 
     Func<SurveyUserView, bool> invokeList = surveyUserView => surveyUserView.deleted != "deleted"); 

     if (filters.ContainsKey("RegionFilter")) 
     { 
      invokeList += surveyUserView => surveyUserView.Region == filters["RegionFilter"]); 
     } 

     if (filters.ContainsKey("LanguageFilter")) 
     { 
      invokeList += surveyUserView => surveyUserView.Locale == filters["LanguageFilter"]; 
     } 

     if (filters.ContainsKey("StatusFilter")) 
     { 
      invokeList += surveyUserView => surveyUserView.Status == filters["StatusFilter"]; 
     } 

     if (filters.ContainsKey("DepartmentFilter")) 
     { 
      invokeList += surveyUserView => surveyUserView.department == filters["DepartmentFilter"]); 
     } 

     return invokeList; 
    } 
    ... 
    Func<SurveyUserView, bool> resultFilter = suv => _getFilterLabda(filters) 
     .GetInvocationList() 
     .Cast<Func<SurveyUserView, bool>>() 
     .All(del => del(suv)) 
+0

Tek bir Func 'yeterli, çok noktaya yayın delegelerinin bir listesine ihtiyacınız yok. 'GetInvocationList()' üyesine bakın. –

+0

@BenVoigt, amacınız bu "bool" işlevlerinden sonuçları birleştirmekse, _can_ '+ =' kullanarak tek bir monolitik delege oluşturmanıza rağmen, aslında doğrudan aramak istemediğiniz bir temsilci oluşturuyor olmalısınız Bana göre bakım açısından oldukça kafa karıştırıcı görünüyor. Bir 'List >' kullanmak oldukça temiz bir şekilde önler. –

+0

@BenVoigt Teşekkürler ... Değiştirildi –

1

Burada soran ne gerçekleştirerek sevdiğim yöntemdir.

private Func<SurveyUserView, bool> _getFilterLambda(IDictionary<string, string> filters) 
{ 
    List<Func<SurveyUserView, bool>> invokeList = new List<Func<SurveyUserView, bool>>(); 

    invokeList.Add((SurveyUserView surveyUserView) => surveyUserView.deleted != "deleted"); 

    if (filters.ContainsKey("RegionFilter")) 
    { 
     invokeList.Add((SurveyUserView surveyUserView) => surveyUserView.Region == filters["RegionFilter"]); 
    } 

    if (filters.ContainsKey("LanguageFilter")) 
    { 
     invokeList.Add((SurveyUserView surveyUserView) => surveyUserView.Locale == filters["LanguageFilter"]); 
    } 

    if (filters.ContainsKey("StatusFilter")) 
    { 
     invokeList.Add((SurveyUserView surveyUserView) => surveyUserView.Status == filters["StatusFilter"]); 
    } 

    if (filters.ContainsKey("DepartmentFilter")) 
    { 
     invokeList.Add((SurveyUserView surveyUserView) => surveyUserView.department == filters["DepartmentFilter"]); 
    } 

    return delegate (SurveyUserView surveyUserView) 
    { 
     bool unfiltered = true; 

     foreach(var filter in invokeList) 
     { 
      unfiltered = unfiltered && filter(surveyUserView); 
     } 

     return unfiltered; 
    }; 
} 

Uygulamak istediğiniz delege öğelerinin listesini oluşturuyoruz; ve daha sonra, filtrelerin her birini basit mantıksal AND ile birleştiren bu liste üzerinde yinelenen ayrı bir temsilci döndürün.

Bu, çalıştığımız temsilci invokeList üzerinden kapatıldığı için çalışır; iade edilen delege ile seyahat eden tüm yeni delegeleri saklayan bir tür özel değişken oluşturmak. Biraz daha yakın sözdizimsel orijinal etmektir

bir alternatiftir: biz gerçekten sadece delegeler için bir liste olarak invokeList kullandığınız Bu versiyonda

private Func<SurveyUserView, bool> _getFilterLambda(IDictionary<string, string> filters) 
{ 
    Func<SurveyUserView, bool> invokeList = (SurveyUserView surveyUserView) => surveyUserView.deleted != "deleted"; 

    if (filters.ContainsKey("RegionFilter")) 
    { 
     invokeList += (SurveyUserView surveyUserView) => surveyUserView.Region == filters["RegionFilter"]; 
    } 

    if (filters.ContainsKey("LanguageFilter")) 
    { 
     invokeList += (SurveyUserView surveyUserView) => surveyUserView.Locale == filters["LanguageFilter"]; 
    } 

    if (filters.ContainsKey("StatusFilter")) 
    { 
     invokeList += (SurveyUserView surveyUserView) => surveyUserView.Status == filters["StatusFilter"]; 
    } 

    if (filters.ContainsKey("DepartmentFilter")) 
    { 
     invokeList += (SurveyUserView surveyUserView) => surveyUserView.department == filters["DepartmentFilter"]; 
    } 

    return delegate (SurveyUserView surveyUserView) 
    { 
     bool unfiltered = true; 

     // implicit cast from Delegate to Func<SurveyUserView, bool> happening on next line 
     foreach (Func<SurveyUserView, bool> filter in invokeList.GetInvocationList()) 
     { 
      unfiltered = unfiltered && filter(surveyUserView); 
     } 

     return unfiltered; 
    }; 
} 

; Çok oyunculu delege yapmak için bir araya getirilen tüm delegelerin bir listesini almak için GetInvocationList() yöntemini (Delegate sınıfı Func türetilmiş bir yöntem) çağırırız.

İlk sürümü kişisel olarak tercih ediyorum çünkü sahnelerin ardında neler olduğu net.

Bunların her ikisi de, yanıt vermeden önce bir şekilde cevapladığım Jacob Seleznev's answer ile aynıdır. Son delegeyi sadece yöntemin içine getirirler, böylece yöntemin kendisi hala Trey'in orijinal sözleşmesini yerine getirir.

Son olarak, tüm filtreler, hiçbir yan etki olmadan siparişten bağımsızysa, filtreleri paralel olarak çalıştıracak bir sürüm yazabiliriz.

private Func<SurveyUserView, bool> _getFilterLambdaParallel(IDictionary<string, string> filters) 
{ 
    List<Func<SurveyUserView, bool>> invokeList = new List<Func<SurveyUserView, bool>>(); 

    invokeList.Add((SurveyUserView surveyUserView) => surveyUserView.deleted != "deleted"); 

    if (filters.ContainsKey("RegionFilter")) 
    { 
     invokeList.Add((SurveyUserView surveyUserView) => surveyUserView.Region == filters["RegionFilter"]); 
    } 

    if (filters.ContainsKey("LanguageFilter")) 
    { 
     invokeList.Add((SurveyUserView surveyUserView) => surveyUserView.Locale == filters["LanguageFilter"]); 
    } 

    if (filters.ContainsKey("StatusFilter")) 
    { 
     invokeList.Add((SurveyUserView surveyUserView) => surveyUserView.Status == filters["StatusFilter"]); 
    } 

    if (filters.ContainsKey("DepartmentFilter")) 
    { 
     invokeList.Add((SurveyUserView surveyUserView) => surveyUserView.department == filters["DepartmentFilter"]); 
    } 

    return delegate (SurveyUserView surveyUserView) 
    { 
     int okCount = 0; 
     Parallel.ForEach(invokeList, delegate (Func<SurveyUserView, bool> f) 
     { 
      if (f(surveyUserView)) 
      { 
       System.Threading.Interlocked.Increment(ref okCount); 
      } 
     }); 
     return okCount == invokeList.Count; 
    }; 
} 

Paralel.ForEach'i, filtreleri paralel olarak yürütmek için kullanırız. Basit boolean'ımızı kullanmamızı engelleyen hafif bir komplikasyon var VE - mantıklı AND'ın atomik olarak kötü bir yarış durumu yaratacağının garantisi yok.

Bunu düzeltmek için atomik olduğu garanti edilen Interlocked.Increment kullanılarak geçirilen filtrelerin sayısını basitçe sayabiliriz. Tüm filtreler başarılı bir şekilde geçtiyse, doğru döndürebileceğimizi biliyoruz; aksi halde ve başarısız olurdu.

Mantıksal bir iş yapmak için eşdeğeri VEYA, okCount ile sıfırdan fazla kontrol etmek zorundadır.