tags:

views:

412

answers:

5
+1  Q: 

Obfuscated C# Code

Years ago their used to be a contest to see who could produce the most obfuscated C code, and some of the results were dramatically unreadable. C was like that. You could really screw things up with the preprocessor in particular.

However, many of the newer features of C# offer an amazing opportunity to obfuscate code to. I was wondering if anyone had an opinion on finding the right balance between concision and clarity in code. Let me offer one example for discussion, the task of filling items into a ListView. (Yes I know you can do it with data binding, but go with me here.)

The control is two column to be filled with an array of struct Person { public string name; public string address; };

One, clear and simple way is this:

private void Fill(Person[] people)
{
    foreach(Person person in people)
    {
        string[] columns = new string[2];
        columns[0] = person.name;
        columns[1] = person.address;            
        ListViewItem item = new ListViewItem(columns);
        listView1.items.Add(item);
    }
}

Clear and simple to understand.

I could also write it like this:

private void Fill(Person[] people)
{
    foreach(Person person in people)
    {
        string[] columns = new string[] { person.name, person.address };
        ListViewItem item = new ListViewItem(columns);
        listView1.items.Add(item);
    }
}

or even:

private void Fill(Person[] people)
{
    foreach(var person in people) // Note use implicit typing here
    {
        listView1.items.Add(new ListViewItem(
        new string[] { person.name, person.address }));
    }
}

Finally, I could also write it like this:

private void Fill(Person[] people)
{
    Array.ForEach(people, item =>
    listView1.items.Add(new ListViewItem(
    new string[] { person.name, person.address}));
}

Each uses various new features of the language to a greater or lesser extent. How do you find the balance between concision and clarity? Should we have an annual Obfuscated C# contest?

+16  A: 

You know what's hard? Writing code that others can read and maintain. Any idiot can write code that compiles and is impossible to maintain.

Always favor maintainability: that's how you find the balance.

Edit:

"Any fool can write code that a computer can understand. Good programmers write code that humans can understand."

  • Martin Fowler, Refactoring: Improving the Design of Existing Code

Thanks to roygbiv for finding the above quote. Apologies to Fowler for murdering his quote; I knew I'd read it before, I just couldn't remember where.

Esteban Araya
+1, amen to that.
Paolo
+1 Good answer. Very different from mine....but good answer.
Mark Brittingham
So is it better to write code that only a kid fresh from college could understand? After all, he might be the one maintaining your code.
Fred Thomas
Are you working on a project with ten crappy programmers? Then yeah, I guess you should write code that they can understand easily, but one might also say that you should work on a different project. Otherwise, probably not.
mquander
@Fred Thomas: Yup, pretty much. I'm sure you, and your employer will both be very happy because of it.
Esteban Araya
+1  A: 

At least with respect to the example that you provide here, I don't really think Obfuscation rises as you proceed until you get to the last one. Even there, the only reason for any ambiguity is the presence of the Lambda and that just takes some getting used to. So, a newbie might have trouble with the last but shouldn't find the others unreadable in the way that the old wild C competition entries were unreadable.

The difference is that these C# examples are all at the same level of abstraction - the more concise examples just remove "fluff." In C, you have the opportunity for ambiguity due to A) arbitrarily renamed/aliased constructs and B) several levels of memory access bundled into one statement.

One the whole, then, you can right obscure code in ANY language but I don't think that C# is prone to it like C and, indeed, I think it is a clearer language than many - even when using some of the more advanced constructs.

Mark Brittingham
Actually, I disagree. The difference is that in the earlier examples the intermediates are named and identified. For example, in the first example, we have a variable named columns that tells you what the string array represents. Then you have an intermediate item which (along with its type) tells you what it represents. And then at the end you are inserting the item.Which is to say, the earlier examples break it down into smaller steps, whereas the latter example combine all the steps together into one.Is it better to have many simple pieces or one more complicated piece?
Fred Thomas
That can be true in bigger compositions, but here, each intermediate item is trivial; i.e. "a list item with the name and address." Having that bound to the name "item" doesn't really add much value.
mquander
I have to agree with mquander - there comes a point where intermediates are so trivial that it is almost more difficult to slog through all of the names. You want to add all of the people to a list...do you really need four-five lines of intermediate declarations and assignments just to do that? Not in my book. Thanks for commenting tho...
Mark Brittingham
I guess I could have provided a better example, but there are limits on what you can do in this format. I am an ex C++ person, and I just notice a lot of these intermediate products being inserted inline (such as the new string[] {...} thing. I suppose that is one of the consequences of the garbage collector, but that sort of thing tends to go in intermediates, and I am not sure all that inlining is an improvement.Thanks for your feedback.
Fred Thomas
+4  A: 

Stuffing everything into one line doesn't make it "obfuscated" -- it just makes you scroll a lot unnecessarily. It would still be trivial for anyone who knows C# to understand any of the examples you presented, and if you used linebreaks, none would really be much better or worse than the others.

mquander
Yeah, the sample the OP provided can hardly be called obfuscation.
Esteban Araya
+1  A: 

C# and VB.NET languages were designed for more for clarity because they are operate at a higher level than C. C is programming close the metal so-to-speak. It's not possible by-design to write obfuscated C# like that of C.

"Any fool can write code that a computer can understand. Good programmers write code that humans can understand."

  • Martin Fowler, Refactoring: Improving the Design of Existing Code
A: 

Code for maximum readability, but:

  1. Remember that superfluous verbosity and syntactic noise hurt readability. More conciseness can coincide with improved readability if the more concise notation allows you to express your intent more directly. For example, compare real lambda functions to simulating them with single-method interfaces.

  2. Assume that other people who read your code are decent programmers and know the language you're working in. Don't assume a language lawyer level of knowledge, but assume a good working knowledge. Don't code to the lowest common denominator because, while it may make your code more maintainable by code monkeys, it will annoy both you and maintenance programmers who actually know what they're doing.

More specifically, example 1 has way too much syntactic noise for something so simple. Example 4 is very difficult for a human to parse. I'd say 2 and 3 are both pretty good, though in the case of example 3 I'd reformat it a little, just to make it easier for a human to parse all the function call nesting:

private void Fill(Person[] people)
{
    foreach(var person in people)
    {
        listView1.items.Add(
            new ListViewItem(
                new string[] { person.name, person.address }
            )
         );
    }
}

Now you have the best of both worlds: It can be easily parsed by humans and doesn't have any superfluous, unnecessarily verbose temporary variables.

Edit: I also think that using implicit variable typing, i.e. var is fine most of the time. People write perfectly readable code in dynamic languages where implicit typing is the only kind of typing, and most of the time the formal types of your variables is a low-level detail that has little to do with the high-level intent of your code.

dsimcha