Любые планы для оператора "do" /Action LINQ?

Вот простой метод с циклом foreach:

IEnumerable<XElement> FieldsToXElements(object instance)
{
    var fieldElements = new List<XElement>();

    foreach (var field in instance.GetType().GetFields(instance))
    {
        fieldElements.Add(new XElement(field.Name, field.GetValue(instance)));
    }

    return fieldElements;
}

Вид уродливый. Если в LINQ был некоторый оператор, означающий "сделать что-то" (например, выполнить Action для каждого из выбранных в заявлении LINQ), он будет выглядеть более красивым, более кратким:

IEnumerable<XElement> FieldsToXElements(object instance)
{
    var fieldElements = new List<XElement>();

    from field in instance.GetType().GetFields(instance))
    let name = field.Name
    let value = field.GetValue(instance)
    do fieldElements.Add(new XElement(name, value));

    return fieldElements;
}

Я понимаю это субъективное, и только мое мнение. Для цикла foreach, который имеет одну строку, которая просто вызывает метод, оператор "do" имеет смысл, на мой взгляд. Но мне интересно, думал ли кто-нибудь в MS о том же. Является ли такой оператор LINQ запланированным в любых будущих выпусках (например, вместе с дебютом С# 4.0)?

Вот еще один пример с предикатом, где фиктивный оператор do действительно сделает код более чистым. Это:

IEnumerable<XElement> FieldsToXElements
    (object instance, Func<FieldInfo, bool> predicate)
{
    var fieldElements = new List<XElement>();

    foreach (var field in instance.GetType().GetFields(instance).Where(predicate))
    {
        fieldElements.Add(new XElement(field.Name, field.GetValue(instance)));
    }

    return fieldElements;
}

против. это:

IEnumerable<XElement> FieldsToXElements
    (object instance, Func<FieldInfo, bool> predicate)
{
    var fieldElements = new List<XElement>();

    from field in instance.GetType().GetFields(instance))
    let name = field.Name
    let value = field.GetValue(instance)
    where predicate(field)
    do fieldElements.Add(new XElement(name, value));

    return fieldElements;
}

Ответ 1

Нет, я не ожидаю, что какой-либо прямой язык suport (т.е. внутри синтаксиса запроса) в ближайшее время.

Похоже, вы имеете в виду мифический метод расширения ForEach; тривиально добавить, но Эрик Липперт много раз прокомментировал перекресток функционального кода без побочных эффектов и Action<T> с побочными эффектами. В частности, деревья выражений С# 3.0/.NET 3.5 паршивы в side-effecs (что делает полную поддержку лямбда сложным). Сторона времени выполнения намного лучше в .NET 4.0, но пока неясно, сколько из этого выйдет на язык (компилятор lambda ) в С# 4.0.

Все, что вам нужно (для версии делегата):

public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{   // note: omitted arg/null checks
    foreach(T item in source) { action(item); }
}

Затем из любого запроса вы можете просто использовать .ForEach(x => /* do something */).

Ответ 2

В вашем конкретном примере (заполнение List<XElement>) я сделаю так.

IEnumerable<XElement> FieldsToXElements(object instance)
{
  List<XElement> fieldElements =
  (
    from field in instance.GetType().GetFields(instance))
    let name = field.Name
    let value = field.GetValue(instance)
    select new XElement(name, value)
  ).ToList();  //Another Option is List<T>.AddRange()
  return fieldElements;
}

Также: не забывайте, что List<T> уже реализует .ForEach<T>(), поэтому, чтобы использовать его против любого Enumerable<T>, это все, что вам нужно.

myEnumerable.ToList().ForEach( x => myAction(x) );

Ответ 3

Я не думаю, что это слишком сложно или, может быть, я что-то пропустил...

IEnumerable<XElement> FieldsToXElements(object instance)
{
    return instance.GetType()
                   .GetFields(instance)
                   .Select( f => new XElement( f.Name,
                                               f.GetValue(instance) ) );
}

Ответ 4

Если вы просто смотрите на вызов функции из своей инструкции linq, вы можете уже сделать это, вы можете сделать вызов функции в назначении Let.

var qry = from e in somelist
          let errorcode = DoSomeProcessing(e)
          select e;