【问题标题】:Can I achieve covariance in .NET 3.5, C#我可以在 .NET 3.5、C# 中实现协方差吗
【发布时间】:2012-03-11 20:18:00
【问题描述】:

我有实现interface IXclass X。我还有一个专门用于 X 的存储库类,它使用 lambda 表达式作为参数:

public interface IX
{
}

public class X : IX
{
    ....
}

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

我需要使存储库类与接口IX 一起工作,因此我将IRepository&lt;IX&gt; 添加到正在实现的接口中:

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&lt;IX, bool&gt; 转换为 Func&lt;X, bool&gt;,但由于代码是使用 .NET 3.5 用 C# 3.0 编写的,因此我无法从 4.0 中引入的类型协方差中受益。

一个简单的解决方案是使用Func&lt;X, bool&gt; newFunc = x =&gt; filterFunc(x);,其中filterFunc 的类型为Func&lt;IX, bool&gt;。这将编译并且人们可能期望它运行良好,但我认为它不会。问题是我使用 3rd 方框架来实现过滤器,即 FluentNhibernate。我知道它使用表达式树将传递到 lambda 表达式成员访问条件(如 x =&gt; x.Name == "John")中,以构建原生 SQL 查询(如 WHERE Name = 'John')。上面的解决方案会产生一个Func&lt;X, bool&gt; 不是这样的表达,我担心它会翻译失败。所以我需要创建相同的 lambda 表达式但具有兼容的类型。知道 X 实现了 IX,很明显 Func&lt;IX, bool&gt; 中的任何代码都适用于 X 类型的对象。但是,对我来说并不明显,我该如何执行这种转换。

我认为这可以使用表达式树来完成。我也担心我的表现会受到很大影响。即使我决定为我的场景提供另一种解决方案,我仍然会欣赏将一个 lambda 转换为类似的另一个的建议方法。


编辑:

为了更清楚地了解我遇到的问题,我编写了以下测试,模拟了我所面临的现实场景:

    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&lt;Func&lt;IX, bool&gt;&gt; 转换为具有相同主体 (y =&gt; y.Name = "John") 的 Expression&lt;Func&lt;X, bool&gt;&gt;


编辑 2:

我终于做到了!正确的做法是不要使用Func&lt;X, bool&gt;,而是使用Expression&lt;Func&lt;X, bool&gt;&gt;

 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

【问题讨论】:

  • 你的意思是 .NET 3.5 吗?没有 C# 3.5 这样的东西。
  • 是的,我的意思是 .NET 3.5,但我在某些地方看到过 C# 3.0,我认为我可以将它应用于 3.5。我纠正了自己
  • 我不知道这种差异,感谢您的链接。从现在开始,我将尝试使用正确的术语

标签: c# nhibernate .net-3.5 fluent-nhibernate


【解决方案1】:

一个简单的解决方案是使用Func&lt;X, bool&gt; newFunc = x =&gt; filterFunc(x);,其中filterFunc 的类型为Func&lt;IX, bool&gt;。这将编译并且人们可能期望它运行良好,但我认为它不会。

为什么要假设,什么时候可以测试?它应该工作得很好。毕竟,您将X 类型的参数传递给IX 类型的参数,这不会导致类型安全问题。

然后您需要将IEnumerable&lt;X&gt; 转换为IEnumerable&lt;IX&gt;,例如可以使用Cast 完成:

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

【讨论】:

  • 我认为你误解了我的意思,但我知道 snppet Func&lt;X, bool&gt; newFunc = x =&gt; filterFunc(x); 可以完美运行。我担心 FluentNhibernate 在构建原生 SQL 查询时可能无法解析它。
  • @IvayloSlavov:如果您的方法签名确实是根据Func&lt;X, bool&gt; 而不是Expression&lt;Func&lt;X, bool&gt;&gt;,那么NHibernate 无论如何都无法理解您的代码。一个代表只是一个代表;它不支持自省。 实际上是否使用了表达式树?那将是一个稍微不同的问题,有不同的解决方案。
  • 你建议从Func&lt;X, bool&gt;切换到Expression&lt;Func&lt;X, bool&gt;&gt;吗?
  • @IvayloSlavov:我建议你完全独立于协方差部分来计算你应该为X 做什么。你目前有什么工作吗?您是否验证了正在生成的 SQL?我怀疑如果它现在真的使用Func,你可能会为每个查询拉下整个数据库表!
  • 我用我获得的结果和调用的代码的详细信息编辑了我的帖子。
【解决方案2】:

为什么你使用你的具体类型不够 IX:

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

【讨论】:

  • 我担心不,因为实现必须通过具体类型才能使 NHibernate 工作。
  • 啊,好吧,我错过了“NHibernate”;)。
【解决方案3】:

正如我正确理解的那样,协方差是一种语言特征。所以它不直接依赖于.net 4。

【讨论】:

  • 它是一个语言特性、一个框架特性(所涉及的委托/接口必须声明为协变/逆变)和一个 CLR 特性(自 . NET 2.0)。
猜你喜欢
  • 1970-01-01
  • 2011-03-06
  • 2012-03-01
  • 2013-05-15
  • 2013-01-12
  • 1970-01-01
  • 2014-01-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多