2012-03-11 34 views
2

我有class X,它實現了interface IX。我也有專門爲X的存儲庫類,它使用拉姆達expresions作爲參數:我可以在.NET 3.5中實現協變,C#

public interface IX 
{ 
} 

public class X : IX 
{ 
    .... 
} 

public class XRepository : IRepository<X> 
{ 
    public IEnumerable<X> Filter(Func<X, bool> filterFunc) 
    { 
     ... 
    } 
} 

我需要讓庫類的工作與接口IX,所以我加IRepository<IX>的接口正在實施:

public class XRepository : IRepository<X>, IRepository<IX> 
{ 
    public IEnumerable<X> Filter(Func<X, bool> filterFunc) 
    { 
     ... 
    } 
    public IEnumerable<IX> Filter(Func<IX, bool> filterFunc) 
    { 
     // I need to call the same filter method as above, but 
     // in order to do so I must convert the Func<IX, bool> to Func<X, bool>. 
    } 
} 

我必須將Func<IX, bool>轉換爲Func<X, bool>,但由於代碼是使用.NET 3.5編寫的C#3.0,因此我無法從4.0中引入的類型協方差中受益。

一個簡單的解決方案可以使用Func<X, bool> newFunc = x => filterFunc(x);,其中filterFuncFunc<IX, bool>類型。這會編譯,有人可能會期望它運行良好,但我認爲它不會。問題是我正在使用第三方框架來實現過濾器,即FluentNhibernate。我知道它使用表達式樹來剝離傳遞到lambda表達式成員訪問條件(如x => x.Name == "John")以構建本機SQL查詢(如WHERE Name = 'John')。上述解決方案將產生一個不是這種表達式的Func<X, bool>,我擔心它將無法翻譯。所以我需要創建相同的lambda表達式,但使用兼容類型。知道X實現了IX,顯然Func<IX, bool>中的任何代碼都可以用於X類型的對象。然而,對我來說這並不明顯,我怎樣才能執行這種轉換。

我認爲這可以使用表達式樹來完成。我也擔心我的表現會受到很大的影響。即使我決定對我的場景採用另一種解決方案,我仍然會欣賞將一個lambda翻譯成另一個類似的方法的建議方法。


編輯:

爲了澄清更多關於我遇到的問題,我寫了下面的測試,模擬現實生活中的scenarion我面對:

Func<IX, bool> filter = y => y.Name == "John"; 
    Func<X, bool> compatibleFilter = y => filter(y); 


    ... 
    // Inside the Filter(Func<X, bool> filter method) 
    using(var session = nhibernateSessionFactory.OpenSession()) 
    { 

     IEnumerable<X> xx = session.Query<X>().Where(z => compatibleFilter(z)).ToList(); 
    } 

如此,在ToList()方法我收到以下異常

Unable to cast object of type 'NHibernate.Hql.Ast.HqlParameter' to type 'NHibernate.Hql.Ast.HqlBooleanExpression'. 

這證實了我的假設,即Flunet NHiberante無法正確處理compatibleFilter參數。

所以我想要的是一種將Func轉換爲Func的方式,或者如John Skeet所建議的Expression<Func<IX, bool>>Expression<Func<X, bool>>,它們具有相同的主體(y => y.Name = "John")。


編輯2:

最後我做了這一點!正確的方法不是使用Func<X, bool>,而是使用Expression<Func<X, bool>>

Expression<Func<IX, bool>> filter = y => y.Name == "John Skeet"; 
Expression<Func<X, bool>> compatibleFilter = Expression.Lambda<Func<X, bool>>(
    filter.Body, 
    filter.Parameters); 

這產生了正確的SQL查詢。IX,bool

+0

待辦事項你的意思是.NET 3.5?沒有C#3.5這樣的東西。 – 2012-03-11 20:24:02

+0

是的,我的意思是.NET 3.5,但是我在某些地方見過C#3.0,我認爲我可以將它應用於3.5。我糾正自己 – 2012-03-11 20:31:07

+0

請參閱http://stackoverflow.com/questions/247621/what-are-the-correct-version-numbers-for-c – 2012-03-11 20:33:47

回答

3

直接取決於一個簡單的解決方案是使用Func<X, bool> newFunc = x => filterFunc(x);其中filterFunc是Func<IX, bool>類型。這會編譯,有人可能會期望它運行良好,但我認爲它不會。

爲什麼假設,什麼時候可以測試?它應該工作絕對好。畢竟,對於類型爲IX的參數,您傳遞的參數類型爲X,這不會導致類型安全性問題。

然後,您需要從IEnumerable<X>轉換爲IEnumerable<IX>,可與Cast來完成,例如:

public IEnumerable<IX> Filter(Func<IX, bool> filterFunc) 
{ 
    Func<X, bool> newFilter = x => filterFunc(x); 
    return Filter(newFilter).Cast<IX>(); 
} 
+0

我覺得你讓我誤解了這一點,但我知道snipet'Func newFunc = x => filterFunc(x);'會很好地工作。在構建本機SQL查詢時,我擔心FluentNhibernate可能無法解析它。 – 2012-03-11 20:29:33

+0

@IvayloSlavov:如果你的方法簽名確實是用'Func '而不是'Expression >',那麼NHibernate無法理解你的代碼。代表只是一個代表;它不支持內省。它實際上是否使用表達式樹?這將是一個稍微不同的問題,採用不同的解決方案。 – 2012-03-11 20:31:17

+0

你是否建議從'Func '切換到'Expression >'? – 2012-03-11 20:34:54

0

據我瞭解,協方差是一種語言功能。因此,它不會對.NET 4

+4

它是一種語言功能,框架功能(涉及的委託/接口必須*聲明*是協變/逆變的)和CLR功能(自.NET 2.0)。 – 2012-03-11 20:26:40

0

爲什麼你用你的具體類型的心不是足夠IX:

public class IXRepository : IRepository<IX> 
{ 
    public IEnumerable<X> Filter(Func<IX, bool> filterFunc) 
    { 
     ... 
    } 
} 
+0

我不擔心,因爲實現必須通過具體類型才能使NHibernate工作。 – 2012-03-11 20:32:44

+0

嗯,我錯過了「NHibernate」;)。 – 2012-03-11 20:39:13

相關問題