- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
假设我有一些函数 c
返回 Expression
:
Func<int, Expression<Func<int>>> c = (int a) => () => a + 3;
现在我想创建另一个 Expression
, 但在创建过程中我想调用函数 c
并将其结果作为新表达式的一部分嵌入:
Expression<Func<int>> d = () => 2 + c(3);
我不能这样做,因为它会解释 c(3)
就像要转换为表达式的函数调用,我会得到无法添加的错误 int
和 Expression<Func<int>>
我想要d
具有以下值(value):
(Expression<Func<int>>)( () => 2 + 3 + 3 )
我也有兴趣让它适用于更复杂的表达式,而不仅仅是这个玩具示例。
你会如何用 C# 实现?
或者,您如何使用我可以在我的 C# 项目中使用的任何其他 CLR 语言来尽可能少地完成它?
更复杂的例子:
Func<int, Expression<Func<int>>> c = (int a) => () => a*(a + 3);
Expression<Func<int, int>> d = (x) => 2 + c(3 + x);
3+x
即使它出现在 c
的正文中,也应该在结果表达式中只计算一次在两个地方。
我有一种强烈的感觉,它不能在 C# 中实现,因为将 lambda 分配给 Expression
由编译器完成,有点像编译时间 const
表达文字。这类似于让编译器理解纯字符串文字 "test"
理解模板字符串文字 "test ${a+b} other"
并且 C# 编译器尚未处于开发的这个阶段。
所以我的主要问题实际上是:
哪种 CLR 语言支持的语法可以让我方便地构建由其他函数构建的表达式树嵌入部分?
其他可能性是一些库可以帮助我使用某种运行时编译模板以这种方式构建表达式树,但我猜这样我会失去表达式代码的代码完成。
似乎 F# 具有“引用”和“取消引用”(拼接)代码的能力:
https://learn.microsoft.com/en-us/dotnet/articles/fsharp/language-reference/code-quotations
最佳答案
对于您的两个示例,这实际上可以通过两个表达式访问者来完成(代码已注释):
static class Extensions {
public static TResult FakeInvoke<TResult>(this Delegate instance, params object[] parameters)
{
// this is not intended to be called directly
throw new NotImplementedException();
}
public static TExpression Unwrap<TExpression>(this TExpression exp) where TExpression : Expression {
return (TExpression) new FakeInvokeVisitor().Visit(exp);
}
class FakeInvokeVisitor : ExpressionVisitor {
protected override Expression VisitMethodCall(MethodCallExpression node) {
// replace FakeInvoke call
if (node.Method.Name == "FakeInvoke") {
// first obtain reference to method being called (so, for c.FakeInvoke(...) that will be "c")
var func = (Delegate)Expression.Lambda(node.Arguments[0]).Compile().DynamicInvoke();
// explore method argument names and types
var argumentNames = new List<string>();
var dummyArguments = new List<object>();
foreach (var arg in func.Method.GetParameters()) {
argumentNames.Add(arg.Name);
// create default value for each argument
dummyArguments.Add(arg.ParameterType.IsValueType ? Activator.CreateInstance(arg.ParameterType) : null);
}
// now, invoke function with default arguments to obtain expression (for example, this one () => a*(a + 3)).
// all arguments will have default value (0 in this case), but they are not literal "0" but a reference to "a" member with value 0
var exp = (Expression) func.DynamicInvoke(dummyArguments.ToArray());
// this is expressions representing what we passed to FakeInvoke (for example expression (x + 3))
var argumentExpressions = (NewArrayExpression)node.Arguments[1];
// now invoke second visitor
exp = new InnerFakeInvokeVisitor(argumentExpressions, argumentNames.ToArray()).Visit(exp);
return ((LambdaExpression)exp).Body;
}
return base.VisitMethodCall(node);
}
}
class InnerFakeInvokeVisitor : ExpressionVisitor {
private readonly NewArrayExpression _args;
private readonly string[] _argumentNames;
public InnerFakeInvokeVisitor(NewArrayExpression args, string[] argumentNames) {
_args = args;
_argumentNames = argumentNames;
}
protected override Expression VisitMember(MemberExpression node) {
// if that is a reference to one of our arguments (for example, reference to "a")
if (_argumentNames.Contains(node.Member.Name)) {
// find related expression
var idx = Array.IndexOf(_argumentNames, node.Member.Name);
var argument = _args.Expressions[idx];
var unary = argument as UnaryExpression;
// and replace it. So "a" is replaced with expression "x + 3"
return unary?.Operand ?? argument;
}
return base.VisitMember(node);
}
}
}
可以这样使用:
Func<int, Expression<Func<int>>> c = (int a) => () => a * (a + 3);
Expression<Func<int, int>> d = (x) => 2 + c.FakeInvoke<int>(3 + x);
d = d.Unwrap(); // this is now "x => (2 + ((3 + x) * ((3 + x) + 3)))"
简单案例:
Func<int, Expression<Func<int>>> c = (int a) => () => a + 3;
Expression<Func<int>> d = () => 2 + c.FakeInvoke<int>(3);
d = d.Unwrap(); // this is now "() => 2 + (3 + 3)
有多个参数:
Func<int, int, Expression<Func<int>>> c = (int a, int b) => () => a * (a + 3) + b;
Expression<Func<int, int>> d = (x) => 2 + c.FakeInvoke<int>(3 + x, x + 5);
d = d.Unwrap(); // "x => (2 + (((3 + x) * ((3 + x) + 3)) + (x + 5)))"
请注意,FakeInvoke 不是类型安全的(您应该显式设置返回类型和参数,而不是检查)。但这只是举例,在实际使用中您可以创建许多 FakeInvoke 重载,如下所示:
public static TResult FakeInvoke<TArg, TResult>(this Func<TArg, Expression<Func<TResult>>> instance, TArg argument) {
// this is not intended to be called directly
throw new NotImplementedException();
}
应该稍微修改上面的代码以正确处理此类调用(因为参数现在不在单个 NewArrayExpression 中),但这很容易做到。有了这样的重载,你可以这样做:
Expression<Func<int, int>> d = (x) => 2 + c.FakeInvoke(3 + x); // this is type-safe now, you cannot pass non-integer as "3+x", nor you can pass more or less arguments than required.
关于c# - 从 lambda 创建表达式树时如何为 'unquote'?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40810227/
SQLite、Content provider 和 Shared Preference 之间的所有已知区别。 但我想知道什么时候需要根据情况使用 SQLite 或 Content Provider 或
警告:我正在使用一个我无法完全控制的后端,所以我正在努力解决 Backbone 中的一些注意事项,这些注意事项可能在其他地方更好地解决......不幸的是,我别无选择,只能在这里处理它们! 所以,我的
我一整天都在挣扎。我的预输入搜索表达式与远程 json 数据完美配合。但是当我尝试使用相同的 json 数据作为预取数据时,建议为空。点击第一个标志后,我收到预定义消息“无法找到任何内容...”,结果
我正在制作一个模拟 NHL 选秀彩票的程序,其中屏幕右侧应该有一个 JTextField,并且在左侧绘制弹跳的选秀球。我创建了一个名为 Ball 的类,它实现了 Runnable,并在我的主 Draf
这个问题已经有答案了: How can I calculate a time span in Java and format the output? (18 个回答) 已关闭 9 年前。 这是我的代码
我有一个 ASP.NET Web API 应用程序在我的本地 IIS 实例上运行。 Web 应用程序配置有 CORS。我调用的 Web API 方法类似于: [POST("/API/{foo}/{ba
我将用户输入的时间和日期作为: DatePicker dp = (DatePicker) findViewById(R.id.datePicker); TimePicker tp = (TimePic
放宽“邻居”的标准是否足够,或者是否有其他标准行动可以采取? 最佳答案 如果所有相邻解决方案都是 Tabu,则听起来您的 Tabu 列表的大小太长或您的释放策略太严格。一个好的 Tabu 列表长度是
我正在阅读来自 cppreference 的代码示例: #include #include #include #include template void print_queue(T& q)
我快疯了,我试图理解工具提示的行为,但没有成功。 1. 第一个问题是当我尝试通过插件(按钮 1)在点击事件中使用它时 -> 如果您转到 Fiddle,您会在“内容”内看到该函数' 每次点击都会调用该属
我在功能组件中有以下代码: const [ folder, setFolder ] = useState([]); const folderData = useContext(FolderContex
我在使用预签名网址和 AFNetworking 3.0 从 S3 获取图像时遇到问题。我可以使用 NSMutableURLRequest 和 NSURLSession 获取图像,但是当我使用 AFHT
我正在使用 Oracle ojdbc 12 和 Java 8 处理 Oracle UCP 管理器的问题。当 UCP 池启动失败时,我希望关闭它创建的连接。 当池初始化期间遇到 ORA-02391:超过
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 9 年前。 Improve
引用这个plunker: https://plnkr.co/edit/GWsbdDWVvBYNMqyxzlLY?p=preview 我在 styles.css 文件和 src/app.ts 文件中指定
为什么我的条形这么细?我尝试将宽度设置为 1,它们变得非常厚。我不知道还能尝试什么。默认厚度为 0.8,这是应该的样子吗? import matplotlib.pyplot as plt import
当我编写时,查询按预期执行: SELECT id, day2.count - day1.count AS diff FROM day1 NATURAL JOIN day2; 但我真正想要的是右连接。当
我有以下时间数据: 0 08/01/16 13:07:46,335437 1 18/02/16 08:40:40,565575 2 14/01/16 22:2
一些背景知识 -我的 NodeJS 服务器在端口 3001 上运行,我的 React 应用程序在端口 3000 上运行。我在 React 应用程序 package.json 中设置了一个代理来代理对端
我面临着一个愚蠢的问题。我试图在我的 Angular 应用程序中延迟加载我的图像,我已经尝试过这个2: 但是他们都设置了 src attr 而不是 data-src,我在这里遗漏了什么吗?保留 d
我是一名优秀的程序员,十分优秀!