以下两个例子以不同的方式做同样的事情。我正在比较它们。
为了举例,定义任何基于业务逻辑从ExpandoObject
创建和返回XElement
的方法:
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);
在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的紧密性(在查询中保留对象创建逻辑),但我知道它可能不可行,具体取决于编译器或运行时的功能。
答案 0 :(得分:4)
后一种方法对我来说非常可怕。我相信,每当你每次捕获一个不同的客户时,都必须真正创建一个新的代表,但我个人不会这样做。鉴于你在那里有真实的陈述,为什么不写一个普通的方法?
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;
}
然后使用以下命令查询:
var qClients = xdoc.Root.Element(XKey.clients)
.Elements(XKey.client)
.Select(ToExpando);
我会更多地关注代码的可读性而不是创建委托的性能,这通常很快。我认为没有必要像你现在看起来那样使用几乎一样多的lambdas。考虑一下你在一年后回到这段代码的时间。你真的会发现嵌套的lambda比方法更容易理解吗?
(顺便说一句,将转换逻辑分离成一个方法可以很容易地单独测试......)
编辑:即使你做想要在LINQ表达式中完成所有操作,为什么你如此热衷于创建另一个间接层?只是因为查询表达式不允许语句lambdas?鉴于你只做一个简单的选择,这很容易应付:
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;
});
答案 1 :(得分:2)
确实,您的第二个版本会重复创建新的Func
实例 - 但是,这只是意味着分配一些小对象(闭包)并使用指向函数的指针。与您需要在委托主体中执行的动态查找相比,我认为这不是一个很大的开销(使用dynamic
个对象)。
或者,你可以声明一个像这样的本地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);
这样,您只需创建一个委托,但将执行转换的代码保持在接近实现查询的代码的位置。
另一种选择是使用匿名类型 - 在您的方案中使用ExpandoObject
的原因是什么?匿名类型的唯一限制是您可能无法从其他程序集访问它们(它们是internal
),但使用dynamic
使用它们应该没问题......
您的选择可能如下所示:
select new { OnlineDetails = new { Password = ..., Roles = ... }}
最后,您还可以使用Reflection将匿名类型转换为ExpandoObject
,但这可能效率更低(即很难有效编写)