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