- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
假设我有一个复杂的 lambda 表达式,如下所示:
x => x.A.HasValue || (x.B.HasValue && x.C == q) || (!x.C.HasValue && !x.A.HasValue) || //...expression goes on
我想将其用作 Expression<Func<T,bool>
在(例如 Linq-To-Entities)Queryable.Where方法。我也想在 Enumerable.Where 中使用它方法,但是 Where 方法只接受 Func<T,bool>
,不是 Expression<Func<T,bool>
.
lambda 语法本身可用于生成或者 Expression<Func<T,bool>>
或 Func<T,bool>
(或与此相关的任何委托(delegate)类型),但在这种情况下,它不能一次生成多个委托(delegate)。
例如,我可以这样写:
public Expression<Func<Pair,bool>> PairMatchesExpression()
{
return x => x.A == x.B;
}
尽可能简单地写:
public Func<Pair,bool> PairMatchesDelegate()
{
return x => x.A == x.B;
}
问题是我不能在两种方式中使用完全相同的 lambda 表达式(即 x => x.A == x.B),而不会将其物理复制到具有两种不同返回类型的两个单独的方法中,尽管编译器能够将其编译成任何一个。
换句话说,如果我想在 Queryable
中使用 lambda 表达式方法,那么我必须使用 Expression
方法签名。但是,一旦我这样做了,我就不能将它用作 Func
我尽可能简单地将方法返回类型声明为 Func
.相反,我现在必须调用 Compile
在 Expression
上然后像这样手动缓存结果:
static Func<Pair,bool> _cachedFunc;
public Func<Pair,bool> PairMatchesFunc()
{
if (_cachedFunc == null)
_cachedFunc = PairMatchesExpression().Compile();
return _cachedFunc;
}
是否有解决此问题的方法,以便我可以以更通用的方式使用 lambda 表达式,而无需在编译时将其锁定为特定类型?
最佳答案
不幸的是,我看不出有什么办法可以在编译时真正得到一个Func
。和一个 Expression
来自同一个 lambda。然而,你至少可以封装掉差异,你也可以推迟 Func
的编译。直到第一次使用它。这是一个充分利用事物并可能满足您需求的解决方案,即使它并没有完全满足您真正想要的(Expression
和 Func
的编译时评估)。
请注意,不使用 [DelegateConstraint]
也能正常工作属性(来自 Fody.ExtraConstraints ),但有了它,您将获得构造函数参数的编译时检查。这些属性使类的行为就像它们具有约束一样 where T : Delegate
,目前在 C# 中不受支持,尽管 ILE 支持它(不确定我说的是否正确,但你明白了)。
public class VersatileLambda<[DelegateConstraint] T> where T : class {
private readonly Expression<T> _expression;
private readonly Lazy<T> _funcLazy;
public VersatileLambda(Expression<T> expression) {
if (expression == null) {
throw new ArgumentNullException(nameof(expression));
}
_expression = expression;
_funcLazy = new Lazy<T>(expression.Compile);
}
public static implicit operator Expression<T>(VersatileLambda<T> lambda) {
return lambda?._expression;
}
public static implicit operator T(VersatileLambda<T> lambda) {
return lambda?._funcLazy.Value;
}
public Expression<T> AsExpression() { return this; }
public T AsLambda() { return this; }
}
public class WhereConstraint<[DelegateConstraint] T> : VersatileLambda<Func<T, bool>> {
public WhereConstraint(Expression<Func<T, bool>> lambda)
: base(lambda) { }
}
隐式转换的美妙之处在于,在特定 Expression<Func<>>
的上下文中或 Func<>
是预期的,您根本不需要做任何事情,只需使用它。
现在,给定一个对象:
public partial class MyObject {
public int Value { get; set; }
}
在数据库中是这样表示的:
CREATE TABLE dbo.MyObjects (
Value int NOT NULL CONSTRAINT PK_MyObjects PRIMARY KEY CLUSTERED
);
然后它像这样工作:
var greaterThan5 = new WhereConstraint<MyObject>(o => o.Value > 5);
// Linq to Objects
List<MyObject> list = GetObjectsList();
var filteredList = list.Where(greaterThan5).ToList(); // no special handling
// Linq to Entities
IQueryable<MyObject> myObjects = new MyObjectsContext().MyObjects;
var filteredList2 = myObjects.Where(greaterThan5).ToList(); // no special handling
如果隐式转换不合适,您可以显式转换为目标类型:
var expression = (Expression<Func<MyObject, bool>>) greaterThan5;
请注意,您真的不需要 WhereConstraint
类,或者你可以摆脱 VersatileLambda
通过将其内容移动到 WhereConstraint
, 但我喜欢将两者分开(因为现在你可以使用 VersatileLambda
来返回除 bool
以外的东西)。 (这种差异在很大程度上是我的回答与 Diego 的不同之处。)使用 VersatileLambda
现在看起来像这样(你可以看到我为什么把它包起来):
var vl = new VersatileLambda<Func<MyObject, bool>>(o => o.Value > 5);
我已经确认这对 IEnumerable
非常有效以及IQueryable
,正确地将 lambda 表达式投影到 SQL 中,正如运行 SQL Profiler 所证明的那样。
此外,您可以使用 lambda 无法完成的表达式来做一些非常酷的事情。检查一下:
public static class ExpressionHelper {
public static Expression<Func<TFrom, TTo>> Chain<TFrom, TMiddle, TTo>(
this Expression<Func<TFrom, TMiddle>> first,
Expression<Func<TMiddle, TTo>> second
) {
return Expression.Lambda<Func<TFrom, TTo>>(
new SwapVisitor(second.Parameters[0], first.Body).Visit(second.Body),
first.Parameters
);
}
// this method thanks to Marc Gravell
private class SwapVisitor : ExpressionVisitor {
private readonly Expression _from;
private readonly Expression _to;
public SwapVisitor(Expression from, Expression to) {
_from = from;
_to = to;
}
public override Expression Visit(Expression node) {
return node == _from ? _to : base.Visit(node);
}
}
}
var valueSelector = new Expression<Func<MyTable, int>>(o => o.Value);
var intSelector = new Expression<Func<int, bool>>(x => x > 5);
var selector = valueSelector.Chain<MyTable, int, bool>(intSelector);
您可以创建 Chain
的重载这需要 VersatileLambda
作为第一个参数,并返回一个 VersatileLambda
.现在你真的很兴奋。
关于c# - 有没有办法捕获 lambda 表达式,这样它就不会在编译时被迫采用表达式或委托(delegate)类型的身份?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33399863/
考虑以下代码: template struct list { template list(Args...) { static_assert(sizeof..
考虑以下代码: template struct list { template list(Args...) { static_assert(sizeof..
最近才开始学习"new"OpenGL(可编程而不是固定功能,我从 Nehe 教程中学到的),我想知道自从 OpenGL 4 发布以来学习 OpenGL 3 是否真的有用。 我问的原因是因为我想知道能够
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用资料或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
我想了解如何操作特征向量/矩阵。我想实现最小二乘高斯牛顿算法(因此我学习使用 Eigen 库)。我有一个 1x6 的参数 vector ,每次迭代都需要更新它们。现在,我只想弄清楚函数如何将 vect
关闭。这个问题是opinion-based 。目前不接受答案。 想要改进这个问题吗?更新问题,以便 editing this post 可以用事实和引文来回答它。 . 已关闭 5 年前。 Improv
我发现编写适用于Enums的静态方法非常困难。这是一个非常人为的示例,但假设您想要编写一个方法,该方法采用 Enum 常量并返回下一个声明的常量。我发现(大约一个小时后)你可以按如下方式进行。它可以工
我正在尝试编写一个函数,在某些条件下,将指向结构的指针更改为指向不同的结构。 我的限制是我想保留初始函数签名,该签名将指向指针(而不是特定结构类型)的通用指针作为参数。 这行不通: [nav] In
我正在尝试将 Keras 示例改编为 VAE https://blog.keras.io/building-autoencoders-in-keras.html 我修改了代码,使用有噪声的 mnist
自 JPA 2.0 以来,关系上有 orphanRemoval 属性,它极大地简化了父子关系的更新,并且与级联删除一起允许删除树的整个分支并轻松删除它。 但是,也有一些情况可能被标记为“收养”,即您将
我正在尝试编写一个类,它能够在以后及时调用不带参数的 lambda。我期待 C++17 类模板参数推导以避免需要工厂函数。但是,尝试在不指定类型的情况下实例化对象会失败。我可以很好地使用工厂功能,但我
我怎样才能避免并非所有控制路径都在此处返回容器的事实: enum Type {Int, String}; Container containerFactory(Type
我开始学习 C++ 和 STL。 我有一个问题: 写一个函数模板palindrome,接受一个 vector 参数并返回true或false来检查 vector 是否是回文(12321是回文,1234
我一直在尝试获取一个条目值(代码中的 S1)以将其自身设置为一个值(_attributes 字典中的 STR),但我就是无法让它工作。我想让它成为一个最终的顶层循环,但我在这方面一步一步来,因为我是一
我想做同样的事情 How do I get the number of days between two dates in JavaScript? 但我想对此日期格式执行相同操作:2000-12-31
我想编写一个带有构造函数的 C++ 类,该构造函数将 auto_ptr 作为其参数,以便我可以将类实例从 auto_ptr 初始化为另一个实例: #include class A { public:
我需要一种方法,我可以在其中获取二维数组中的输入并以最快的方式之一对其进行逐行排序。我尝试使用 Insertion Sort 同时获取 Input 和 Sort it。我使用的第二件事是我单独为一行取
好的,我已经阅读了一些关于 IDisposable 最佳实践的文章,我想我基本上明白了(终于)。 我的问题与从 IDisposable 基类继承有关。我看到的所有示例都在子类中一遍又一遍地编写相同的代
定义类时,以下是否有效? T(const T&&) = default; 我正在阅读移动构造函数 here并且它解释了如何仍然可以隐式声明默认值: A class can have multiple
我想使用 LoadLibrary 开发一个插件系统。 我的问题是:我希望我的函数采用 const char* 而 LoadLibrary 采用 LPCTSTR。 我有一个聪明的想法来做(LPCSTR)
我是一名优秀的程序员,十分优秀!