我想说
int x = magic(), y = moremagic();
return i => i + (x/y);
Run Code Online (Sandbox Code Playgroud)
并将x捕获为常量而不是变量引用.这个想法是x永远不会改变,所以当表达式稍后编译时,编译器可以进行常量折叠并产生更有效的代码 - 即x/y通过指针解引用到闭包记录中来计算一次而不是每次调用.
没有办法在方法中将x标记为只读,并且编译器不够聪明,无法检测到在创建表达式后它不会更改.
我不想手工构建表达式.有什么好主意吗?
更新:我最终使用奇妙的LinqKit来构建一个部分评估器,它将执行我想要的替换.只有当您知道相关引用不会改变时,转换才是安全的,但它适用于我的目的.可以通过在其中添加额外的一两个检查来限制部分评估仅限于您控制的闭包的直接成员,这在检查LinqKit中提供的示例代码时非常明显.
/// <summary>Walks your expression and eagerly evaluates property/field members and substitutes them with constants.
/// You must be sure this is semantically correct, by ensuring those fields (e.g. references to captured variables in your closure)
/// will never change, but it allows the expression to be compiled more efficiently by turning constant numbers into true …Run Code Online (Sandbox Code Playgroud) 在阅读有关实体框架性能的文章时,我发现了这条信息:
其次,问题[SQL Server不会重用执行计划]首先发生因为(由于实现细节)将int传递给Skip()和Take()方法,Entity Framework无法查看是否它们传递的绝对值如Take(100)或变量如Take(resultsPerPage),因此它不知道该值是否应该参数化.
建议的解决方案是改变这种代码风格:
var schools = db.Schools
.OrderBy(s => s.PostalZipCode)
.Skip(model.Page * model.ResultsPerPage)
.Take(model.ResultsPerPage)
.ToList();
Run Code Online (Sandbox Code Playgroud)
在这种风格:
int resultsToSkip = model.Page * model.ResultsPerPage;
var schools = db.Schools
.OrderBy(s => s.PostalZipCode)
.Skip(() => resultsToSkip) //must pre-calculate this value
.Take(() => model.ResultsPerPage)
.ToList();
Run Code Online (Sandbox Code Playgroud)
这允许实体框架知道这些是变量,并且生成的SQL应该被参数化,这反过来允许重用执行计划.
我们的应用程序中有一些代码以相同的方式使用变量,但我们必须在运行时构建Expression,因为事先不知道类型.
以下是它过去的样子:
var convertedId = typeof(T).GetConvertedIdValue(id);
var prop = GetIdProperty(typeof(T));
var itemParameter = Expression.Parameter(typeof(T), "item");
var whereExpression = Expression.Lambda<Func<T, bool>>
(
Expression.Equal(
Expression.Property(
itemParameter,
prop.Name
),
Expression.Constant(convertedId)
),
new[] { itemParameter }
); …Run Code Online (Sandbox Code Playgroud)