tags:

views:

618

answers:

4

Here's a simple method with a foreach loop:

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

Kind of ugly. If there were some operator in LINQ that mean "do something" (e.g. perform an Action for each selected in the LINQ statement), it would look nicer, more terse:

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

I realize that's subjective, and just my opinion. For a foreach loop that has one line that simply invokes a method, a "do" operator makes sense, in my opinion. But I'm wondering if anyone at MS has thought the same thing. Is such LINQ operator planned in any upcoming releases (e.g. alongside the C# 4.0 debut)?

Here's another example, with a predicate, where the fictitious do operator would really make the code look cleaner. This:

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

vs. this:

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;
}
+8  A: 

No, I don't expect any direct language suport (i.e. inside the query syntax) any time soon.

It sounds like you mean the mythical ForEach extension method; trivial to add, but Eric Lippert has commented numerous times about the cross between side-effect-free functional code and Action<T> with side-effects. In particular, C# 3.0 / .NET 3.5 expression trees are lousy at side-effecs (making full lambda support tricky). The runtime side is much better in .NET 4.0, but it is unclear at the moment how much of this will make it into the language (lambda compiler) in C# 4.0.

All you need (for delegate version) is:

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

Then from any query you can simply use .ForEach(x => /* do something */).

Marc Gravell
A: 

I don't think this is too hard or maybe I'm missing something...

IEnumerable<XElement> FieldsToXElements(object instance)
{
    return instance.GetType()
                   .GetFields(instance)
                   .Select( f => new XElement( f.Name,
                                               f.GetValue(instance) ) );
}
tvanfosson
The task is trivial. I'm more interested in the "prettiness" of the code. :)
Chris
A: 

If you are simply looking at calling a function from within your linq statement then you can already do that, you can make a call out to a function in a Let assignment.

var qry = from e in somelist
          let errorcode = DoSomeProcessing(e)
          select e;
Tim Jarvis
The only problem here is DoSomeProcessing is a Func<>, not an Action<> :)
Chris
+1  A: 

For your specific example (populating a List<XElement>), I'd do it this way.

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

Also: Don't forget that List<T> already implements .ForEach<T>(), so to use it against any Enumerable<T>, this is all the code you need.

myEnumerable.ToList().ForEach( x => myAction(x) );
David B
Very nice! Didn't think of that. Although I would shorten it slightly by just doing: return (from field in...).ToList() without defining List<XElement> fieldElements. Thanks again!
Chris