views:

187

answers:

5

Okay, in python one can do this:

def foo(monkeys):
    def bar(monkey):
        #process and return new monkey
    processed_monkeys = list()
    for monkey in monkeys:
        processed_monkeys += bar(monkey)
    return processed_monkeys

(This is just a stupid example) I sometimes miss this functionality of declaring a method inside another method in c#. But today I had the following idea for accomplishing this:

List<Monkey> foo(List<Monkey> monkeys)
{
    Func<Monkey, Monkey> bar = delegate(Monkey monkey)
    {
        //process and return new monkey
    }
    List<Monkey> processed_monkeys = new List<Monkey>(monkeys.Count);
    foreach(Monkey monkey in monkeys)
        processed_monkeys.Append(bar(monkey));
    return processed_monkeys;
}

Obviously the workaround does not provide exactly the same functionality as the original as the Func or Action Delegates are limited to 4 parameters, but more than 4 parameters are rarely needed...

What are your opinions on this?
Is this evil and should be avoided for all but very special situations?
How is the performance of this? Will a new bar function be created each time the function is called, or will the compiler optimize this somehow?

+5  A: 

...as the Func or Action Delegates are limited to 4 parameters...

Starting with .NET 4.0, these delegate types are defined for up to something like 17 parameters. You can also define your own quite simply for any arbitrary number of parameters; for example, below I define a delegate that takes 5 parameters:

public delegate void Action<T1, T2, T3, T4, T5>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);

What are your opinions on this?

It's totally fine. Believe or not, .NET developers do this all the time.

Is this evil and should be avoided for all but very special situations?

Nope, it's pretty benign. There's really no harm in it.

How is the performance of this? Will a new bar function be created each time the function is called, or will the compiler optimize this somehow?

The performance is pretty good. The compiler will define an actual full-blown CLR type to provide the anonymous method, just like it defines full-blown CLR types for anonymous types and for methods using the yield keyword. Don't worry; anonymous methods do not incur dynamic compilation on every invocation! Really, that would be pretty absurd, if you think about it.

This isn't even really what I'd call "optimization"; it's simply the way that anonymous methods are implemented by the compiler.

Dan Tao
"The compiler will define an actual method". It actually generates a class to wrap that method.
Richard Szalay
@Richard: Thanks -- updated the answer to reflect that fact.
Dan Tao
+2  A: 

You could use lambda expressions syntax, so the code will be a bit shorter and you won't be limited to 4 arguments.

var bar = (monkey) => { return new Monkey(); }

Imho, it's not evil. The delegate will be definitely compiled once, so the difference in performance between delegate and static method will not be significant.

Of course, just as always, this technique should not be overused to avoid creating unreadable/unmaintanable code.

VladV
+2  A: 

It's evil when it makes your code harder to read, good when it makes it easier to read, neutral otherwise.

Jon Hanna
Nah it's bad when nothing changes, just leave it alone.
Matt Joiner
A: 

I have used this 'pattern' quite a lot in C# after learning Scheme.

It is very handy to create closures that capture values that can be used as a continuation function (especially when used asynchronously).

leppie
A: 

I'de consider it good only if the particular sub function will be used only in this function. Otherwise you'll have to repeat it and it becomes evil to me.

David Brunelle