tags:

views:

311

answers:

4

I just wrote the following function:

public string Ebnf {
    get {
        var props = GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
        var ruleProps = from p in props where p.PropertyType.IsSubclassOf(typeof(ARule)) select p;
        var rules = from p in ruleProps select (ARule)p.GetValue(this, null);
        var ebnfs = from r in rules select r.Name + " = " + r.Ebnf + ".";
        return string.Join("\n", ebnfs.ToArray());
    }
}

I started wondering if Linq actually saved me space, or whether I was using Linq just for the sake of it:

public string EbnfNonLinq {
    get {
        var ebnfs = new List<string>();
        var props = GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
        foreach (var p in props) {
            if (p.PropertyType.IsSubclassOf(typeof(ARule))) {
                var r = (ARule)p.GetValue(this, null);
                ebnfs.Add(r.Name + " = " + r.Ebnf + ".");
            }
        }
        return string.Join("\n", ebnfs.ToArray());
    }
}

7 lines of code vs. 5: it's a savings. But I wonder if the density of the first function is too much. (This is not performance critical code, so I am not concerned about that.)

Which do you think is prettier, more maintainable, comprehensible, better?

+1  A: 

I find the non-linq version slightly more readable.

ggf31416
+6  A: 

Fluffy syntax is fluffy. Terse syntax is terse.

public string Ebnf
{
get {
    var props = GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
    string[] ebnfs = props
      .Where(prop => prop.PropertyType.IsSubclassOf(typeof(ARule)))
      .Select(prop => (ARule)prop.GetValue(this, null))
      .Select(rule => rule.Name + " = " + rule.Ebnf + ".")
      .ToArray();
    return string.Join("\n", ebnfs);
}
}
David B
It is easier to read, but it is a bit harder to debug when there is a problem.
Jonathan Allen
Agreed, but on the other hand this is clear enough that it's pretty easy to tell whether or not there's going to be a problem. (Sure would be nice if LINQ had debugging tools that were better than "use functions that support logging in your lambdas.")
Robert Rossney
+4  A: 

I like the first one a bit better. I think the linq is prettier, but would probably make it the prettieriest by getting entire name in one shot. Something like this, if possible:

var ebnfs = from p in props 
            where p.PropertyType.IsSubclassOf(typeof(ARule)) 
            let rule = (ARule)p.GetValue(this, null)
            select rule.Name + " = " + rule.Ebnf + ".";

 return String.Join(",", ebnfs.ToArray());

^^ warning, untested :)

I don't like using .Where or .Select much, so I stick to naked linq and really just use those when it is the only thing I am doing to a collection.

As the commenter noted, this does make debugging a bit harder in some cases. However, if it is unlikely that there is something odd going on in rule.Name/Ebnf, then that probably won't be a big problem.

Andrew Backer
I *always* forget about `let`. Thanks for the reminder, I think your code is the prettierest so far. :-)
Frank Krueger
Thanks :) The only drawback is that calling .ToArray() is uglified with a wrapping () compared to the .Where.Select.Select.ToArray dot and semicolon party. There should be a "select list-of ...." or "select array-of " operators!
Andrew Backer
+1  A: 

I like the LINQ version because it makes it clear what you are trying to acomplish. Each line is dependent on only the line before it and nothing else.

One comment though, you may want to execute them line by line. Right now each of your varaibles is a query that won't be executed until the last line. That means tracking down bugs may be harder than necessary.

The change I am proposing is to simply add ToList after each query. This executes it immedately.

    var props = GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
    var ruleProps = (from p in props where p.PropertyType.IsSubclassOf(typeof(ARule)) select p).ToList();
    var rules = (from p in ruleProps select (ARule)p.GetValue(this, null)).ToList();
    var ebnfs = (from r in rules select r.Name + " = " + r.Ebnf + ".").ToList();
    return string.Join("\n", ebnfs.ToArray());

Pros: Easier debugging. You can reuse the results without rerunning the query. Cons: If you are using where clauses, sticking to raw queries can sometimes be faster.

Jonathan Allen
I find this bizarre. To force immediate execution, all you need to do is call ToArray() on the very last enumeration which consults the first ones (i.e. in the string.Join()). As-is, you're making four useless lists for no particular reason.
mquander
No, you are making lists so that you can check them each step of the way.
Jonathan Allen
If you need to check them each step of the way when debugging, simply call ToArray() in the Immediate window of the debugger on each variable. There's no reason to add a runtime performance penalty to each line, every time this code is executed, simply to save typing during a one-time debugging session.
Joel Mueller