Эффективность создания экземпляра делегата встроенного запроса LINQ? - PullRequest
1 голос
/ 02 марта 2011

Ниже приведены два примера, которые делают одно и то же по-разному. Я сравниваю их.

Версия 1

Для примера определите любой метод для создания и возврата 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);

Версия 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 за ее ограниченность (сохраняя логику создания объекта в запросе), но я знаю, что она может быть нежизнеспособной в зависимости от того, что делает компилятор или среда выполнения.

Ответы [ 2 ]

4 голосов
/ 02 марта 2011

Последний подход выглядит довольно ужасно для меня.Я полагаю, что каждый раз нужно будет по-настоящему создавать нового делегата, так как вы каждый раз захватываете другого клиента, но лично я бы не стал делать это вообще.Учитывая, что у вас есть реальные утверждения, почему бы не написать нормальный метод?

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);

Я был бы много больше заботятся о читабельности кода, чем о производительности создания делегатов, что обычно довольно быстро.Я не думаю, что есть необходимость использовать почти столько же лямбд, сколько вам сейчас хочется.Подумайте, когда вы вернетесь к этому коду через год.Вы действительно собираетесь найти вложенную лямбду проще для понимания, чем метод?

(Кстати, разделение логики преобразования на метод упрощает тестирование в отдельности ...)

РЕДАКТИРОВАТЬ: Даже если вы do хотите сделать все это в выражении LINQ, почему вы так стремитесь создать еще один уровень косвенности?Только потому, что выражения запроса не допускают лямбда-выражения?Учитывая, что вы делаете только простой выбор, с этим легко справиться:

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; 
           });
2 голосов
/ 02 марта 2011

Это правда, что ваша вторая версия постоянно создает новый экземпляр Func - однако это просто означает выделение небольшого объекта (замыкание) и использование указателя на функцию. Я не думаю, что это большие накладные расходы по сравнению с динамическими поисками, которые необходимо выполнить в теле делегата (для работы с dynamic объектами).

Кроме того, вы можете объявить локальную лямбда-функцию следующим образом:

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, но это, вероятно, будет еще более неэффективно (т. Е. Очень сложно писать эффективно)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...