创建 LINQ 查询内联委托实例的效率?
以下是以不同方式执行同一操作的两个示例。我正在比较它们。
版本 1
作为示例,定义任何方法来根据业务逻辑从 XElement
创建并返回 ExpandoObject
:
var ToExpando = new Func<XElement, ExpandoObject>(xClient =>
{
dynamic o = new ExpandoObject();
o.OnlineDetails = new ExpandoObject();
o.OnlineDetails.Password = xClient.Element(XKey.onlineDetails).Element(XKey.password).Value;
o.OnlineDetails.Roles = xClient.Element(XKey.onlineDetails).Element(XKey.roles).Elements(XKey.roleId).Select(xroleid => xroleid.Value);
// More fields TBD.
}
从 LINQ to XML 查询调用上述委托:
var qClients =
from client in xdoc.Root.Element(XKey.clients).Elements(XKey.client)
select ToExpando(client);
版本 2
在 LINQ 查询中完成所有操作,包括创建和调用 Func 委托。
var qClients =
from client in xdoc.Root.Element(XKey.clients).Elements(XKey.client)
select (new Func<ExpandoObject>(() =>
{
dynamic o = new ExpandoObject();
o.OnlineDetails = new ExpandoObject();
o.OnlineDetails.Password = client.Element(XKey.onlineDetails).Element(XKey.password).Value;
o.OnlineDetails.Roles = client.Element(XKey.onlineDetails).Element(XKey.roles).Elements(XKey.roleId).Select(xroleid => xroleid.Value);
// More fields TBD.
return o;
}))();
考虑到委托创建是在 select
部分中进行的,版本 2 是否效率低下?它是否由 C# 编译器或运行时管理或优化,因此并不重要?
我喜欢版本 2,因为它的紧密性(在查询中保留对象创建逻辑),但我知道它可能不可行,具体取决于关于编译器或运行时的作用。
Following are two examples that do the same thing in different ways. I'm comparing them.
Version 1
For the sake of an example, define any method to create and return an ExpandoObject
from an XElement
based on business logic:
var ToExpando = new Func<XElement, ExpandoObject>(xClient =>
{
dynamic o = new ExpandoObject();
o.OnlineDetails = new ExpandoObject();
o.OnlineDetails.Password = xClient.Element(XKey.onlineDetails).Element(XKey.password).Value;
o.OnlineDetails.Roles = xClient.Element(XKey.onlineDetails).Element(XKey.roles).Elements(XKey.roleId).Select(xroleid => xroleid.Value);
// More fields TBD.
}
Call the above delegate from a LINQ to XML query:
var qClients =
from client in xdoc.Root.Element(XKey.clients).Elements(XKey.client)
select ToExpando(client);
Version 2
Do it all in the LINQ query, including creation and call to Func delegate.
var qClients =
from client in xdoc.Root.Element(XKey.clients).Elements(XKey.client)
select (new Func<ExpandoObject>(() =>
{
dynamic o = new ExpandoObject();
o.OnlineDetails = new ExpandoObject();
o.OnlineDetails.Password = client.Element(XKey.onlineDetails).Element(XKey.password).Value;
o.OnlineDetails.Roles = client.Element(XKey.onlineDetails).Element(XKey.roles).Elements(XKey.roleId).Select(xroleid => xroleid.Value);
// More fields TBD.
return o;
}))();
Considering delegate creation is in the select
part, is Version 2 inefficient? Is it managed or optimized by either the C# compiler or runtime so it won't matter?
I like Version 2 for its tightness (keeping the object creation logic in the query), but am aware it might not be viable depending on what the compiler or runtime does.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
后一种方法对我来说看起来非常可怕。我相信每次捕获不同的客户端时都必须真正创建一个新的委托,但我个人根本不会这样做。既然你已经有了真正的语句,为什么不写一个普通的方法呢?
然后查询它:
我会更更关心代码的可读性而不是创建委托的性能,这通常很快。我认为没有必要像您目前所希望的那样使用尽可能多的 lambda。想象一下一年后您何时再次查看这段代码。您真的会发现嵌套 lambda 比方法更容易理解吗?
(顺便说一句,将转换逻辑分离到一个方法中可以轻松地进行单独测试...)
编辑:即使您确实想要在 LINQ 表达式中完成所有操作,为什么这么想热衷于创建另一个间接级别?仅仅因为查询表达式不允许语句 lambda?鉴于您只是进行简单的选择,因此很容易处理:
The latter approach looks pretty horrible to me. I believe it will have to genuinely create a new delegate each time as you're capturing a different client each time, but personally I wouldn't do it that way at all. Given that you've got real statements in there, why not write a normal method?
and then query it with:
I would be much more concerned about the readability of the code than the performance of creating delegates, which is generally pretty quick. I don't think there's any need to use nearly as many lambdas as you currently seem keen to do. Think about when you come back to this code in a year's time. Are you really going to find the nested lambda easier to understand than a method?
(By the way, separating the conversion logic into a method makes that easy to test in isolation...)
EDIT: Even if you do want to do it all in the LINQ expression, why are you so keen to create another level of indirection? Just because query expressions don't allow statement lambdas? Given that you're doing nothing but a simple select, that's easy enough to cope with:
确实,您的第二个版本会重复创建新的
Func
实例 - 但是,这仅意味着分配一些小对象(闭包)并使用指向函数的指针。与您需要在委托主体中执行的动态查找(使用动态
对象)相比,我认为这并不是一个很大的开销。或者,您可以像这样声明一个本地 lambda 函数:
这样,您可以只创建一个委托,但将执行转换的代码保持在靠近实现查询的代码的位置。
另一种选择是使用匿名类型 - 在您的场景中使用
ExpandoObject
的原因是什么?匿名类型的唯一限制是您可能无法从其他程序集访问它们(它们是内部的),但使用动态处理它们应该没问题。 ..您的选择可能如下所示:
最后,您还可以使用反射将匿名类型转换为
ExpandoObject
,但这可能会更加低效(即很难高效编写)It is true that your second version creates new
Func
instance repeatedly - however, this just means allocating some small object (closure) and using pointer to a function. I don't think this is a large overhead compared to dynamic lookups that you need to perform in the body of the delegate (to work withdynamic
objects).Alternatively, you could declare a local lambda function like this:
This way, you can create just a single delegate, but keep the code that does the conversion close to the code that implements the query.
Another option would be to use anonymous types instead - what are the reasons for using
ExpandoObject
in your scenario? The only limitation of anonymous types would be that you may not be able to access them from other assemblies (they areinternal
), but working with them usingdynamic
should be fine...Your select could look like:
Finally, you could also use Reflection to convert anonymous type to
ExpandoObject
, but that would probably be even more inefficient (i.e. very difficult to write efficiently)