简体   繁体   English

创建LINQ查询的委托实例内联的效率?

[英]Efficiency of creating delegate instance inline of LINQ query?

Following are two examples that do the same thing in different ways. 以下是两个以不同方式执行相同操作的示例。 I'm comparing them. 我正在比较它们。

Version 1 版本1

For the sake of an example, define any method to create and return an ExpandoObject from an XElement based on business logic: 为了举例说明,请定义任何方法基于业务逻辑从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.
}

Call the above delegate from a LINQ to XML query: 从LINQ to XML查询中调用上述委托:

var qClients =
    from client in xdoc.Root.Element(XKey.clients).Elements(XKey.client)
    select ToExpando(client);

Version 2 版本2

Do it all in the LINQ query, including creation and call to Func delegate. 在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;
  }))();

Considering delegate creation is in the select part, is Version 2 inefficient? 考虑到委托创建在select部分中,第2版效率低下吗? Is it managed or optimized by either the C# compiler or runtime so it won't matter? 它是由C#编译器还是运行时进行管理或优化的,所以没关系吗?

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. 我喜欢版本2的紧密性(在查询中保留对象创建逻辑),但是我知道它可能不可行,具体取决于编译器或运行时的功能。

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? 既然那里有真实的语句,为什么不编写普通的方法呢?

private static ToExpando(XElement client)
{
    // Possibly use an object initializer instead?
    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);
    return o;
}

and then query it with: 然后查询:

var qClients = xdoc.Root.Element(XKey.clients)
                        .Elements(XKey.client)
                        .Select(ToExpando);

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. 我认为没有必要使用您目前似乎想使用的几乎所有数量的lambda。 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? 您真的要找到比方法更容易理解的嵌套lambda吗?

(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? 编辑:即使您确实想在LINQ表达式中完成所有操作,为什么还要热衷于创建另一个间接级别? Just because query expressions don't allow statement lambdas? 只是因为查询表达式不允许语句lambda? Given that you're doing nothing but a simple select, that's easy enough to cope with: 鉴于您只需要选择一个简单的方法,那么就很容易处理了:

var qClients = xdoc.Root
           .Element(XKey.clients)
           .Elements(XKey.client)
           .Select(client => {
               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);
               return o; 
           });

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. 的确,您的第二个版本重复创建了新的Func实例-但是,这仅意味着分配一些小对象(关闭)并使用指向函数的指针。 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 with dynamic objects). 与您需要在委托主体中执行的动态查找(使用dynamic对象)相比,我认为这不是很大的开销。

Alternatively, you could declare a local lambda function like this: 另外,您可以像这样声明一个本地lambda函数:

Func<XElement, ExpandoObject> convert = client => {
    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;
}

var qClients =
    from client in xdoc.Root.Element(XKey.clients).Elements(XKey.client)
    select convert(client);

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? 另一种选择是改为使用匿名类型 -在您的方案中使用ExpandoObject的原因是什么? The only limitation of anonymous types would be that you may not be able to access them from other assemblies (they are internal ), but working with them using dynamic should be fine... 匿名类型的唯一限制是您可能无法从其他程序集(它们是internal )访问它们,但是使用dynamic使用它们应该没问题。

Your select could look like: 您的选择可能类似于:

select new { OnlineDetails = new { Password = ..., Roles = ... }}

Finally, you could also use Reflection to convert anonymous type to ExpandoObject , but that would probably be even more inefficient (ie very difficult to write efficiently) 最后,您还可以使用Reflection将匿名类型转换为ExpandoObject ,但这可能会更加低效(即很难高效地编写)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM