Question: I have just wrote my first code using c# lamba expressions. It works, but I am not sure if this is the best way to do it. Any recommendations on a better way to do the lambda expression? It seems odd to have numerous lines of code in the expression like I do below.
Background: I have a generic list of delegates. Each delegate function returns an enum value indicating what happened in the function. Upon evaluation of the delegate, I need to add the enum to a List if it was not a specific enum value.
Disclaimer: Code here is very generic, the real code actually does stuff in the delegates to determine the return value!
class Class1
{
public enum WhatHappened
{
ThingA,
ThingB,
Nothing
}
private delegate WhatHappened del();
public static List<WhatHappened> DoStuff()
{
List<del> CheckValues = new List<del>();
List<WhatHappened> returnValue = new List<WhatHappened> { };
CheckValues.Add(delegate { return method1(); });
CheckValues.Add(delegate { return method2(); });
CheckValues.ForEach(x =>
{
WhatHappened wh = x();
if (wh != WhatHappened.Nothing)
returnValue.Add(wh);
});
return returnValue;
}
private static WhatHappened method1()
{
return WhatHappened.Nothing;
}
private static WhatHappened method2()
{
return WhatHappened.ThingA;
}
}
Note: I originally had the lambda like adding all the items (see below), then removing the ones I didn't want (WhatHappened.Nothing).
CheckValues.ForEach(x => returnValue.Add(x()));
-
In my opinion, based on the example, it looks fine. You could refactor even more by replacing:
CheckValues.Add(delegate { return method1(); }); CheckValues.Add(delegate { return method2(); });with:
CheckValues.Add(() => WhatHappened.Nothing); CheckValues.Add(() => WhatHappened.ThingA); -
You can go lambda all the way by chaining Select (map) and Where (filter) instead of multiple FOR loops and IF statements
// get results from the list of functions var results = CheckValues.Select(x => x()); // filter out only the relevant ones. var returnValues = results.Where(x => x != WhatHappened.Nothing);Basically, you should think more declaratively instead of imperatively when work ing with lambdas. It'll help you write more elegant code.
-
It's a bit more idiomatic to write the following instead of using the delegate keyword. It doesn't change the underlying functionality though.
CheckValues.Add( () => method1() );Also, I find it more readable to rewrite the ForEach as the following
CheckValues = CheckValues. Select(x => x()). Where(wh => wh != WhatHappened.Nothing ). ToList();Jon Skeet : That won't compile unless you've got a ForEach extension method. I also like having my dots at the start of lines :)JaredPar : @Jon, I'm so used to my custom LINQ methods that I forget they don't exist by default. Updated to avoid extension methods i don't own. I honestly go back and forth on dot before or dot after. My normal preference is dot before. But some languages I use require character to be after (PowerShell and VB.Net) so I drift back and forth ;) -
Okay, a few suggestions:
- Don't call your delegate
del. In this case, I'd useFunc<WhatHappened>- but if you do want to declare your own delegate type, give it a more descriptive name, and obey the .NET naming conventions. Instead of using anonymous methods to add to
CheckValues, you can just use:CheckValues.Add(method1); CheckValues.Add(method2);The compiler will convert the method groups into delegates.
I'd recommend not using Pascal case for a local variable name to start with.
- Your collection initializer for
returnValuesisn't really doing anything for you - just call theList<T>constructor as normal, or use my code below which doesn't require a local variable to start with. - If your list really only has two delegates in it, I'd just call them separately. It's a lot simpler.
Otherwise you can indeed use LINQ as Jared suggests, but I'd do it slightly differently:
return CheckValues.Select(x => x()) .Where(wh => wh != WhatHappened.Nothing) .ToList();
EDIT: As suggested, here's the full example. It's not quite the same as Denis's though... I've made a couple of changes :)
public static List<WhatHappened> DoStuff() { var functions = new List<Func<WhatHappened>> { Method1, Method2 }; return functions.Select(function => function()) .Where(result => result != WhatHappened.Nothing) .ToList(); }(I'm assuming that
method1andmethod2have been renamed to fit the naming convention. Of course in real life I'm sure they'd have more useful names anyway...)Mike Ohlsen : I agree with your suggestions, but I think you took the example a little too literally. In the example code I didn't spend time with naming conventions. The real code has more descriptive names.Jon Skeet : If you give an example of your code, and ask us what we would do differently, how are we meant to know the difference between mistakes you *do* know about and mistakes you *don't* know about?Joel Coehoorn : I think the main thing is to use the existing Func rather than your own delegate type. Then the naming is moot.Denis Troller : damn Jon, you beat me to it AND you manage to give better explanations. Does your keyboard read your mind or something ? :)Sean Reilly : Lots of great advice in this answer. I would suggest combining it with the completely rewritten example from Denis Troller's answer. - Don't call your delegate
-
I would simply use Linq, but that's just me:
public static List<WhatHappened> DoStuff() { List<del> CheckValues = new List<del>(); List<WhatHappened> returnValue = new List<WhatHappened>(); CheckValues.Add(method1); CheckValues.Add(method2); return CheckValues .Select(dlg => dlg()) .Where( res => res != WhatHappened.Nothing) .ToList(); }Note that you can also use Func instead of declaring a Delegate type if you want, but that's less terse in that case. Also, I'd return an
IEnumerable<WhatHappened>instead of a List, but it's all about the context. -
Here's a LINQ-free solution:
return CheckValues .ConvertAll<WhatHappened>(x => x()) .FindAll(y => y != WhatHappened.Nothing);caveat
This is not the most performant solution, as it would iterate twice.
-
I can't fathom the purpose of the code.. however here goes.
Used delegate chaining Update: and picked up some Enumerable goodness from Jon n Jared's postsprivate delegate WhatHappened WhatHappenedDelegate(); public static List<WhatHappened> DoStuff() { WhatHappenedDelegate delegateChain = null; delegateChain += method1; delegateChain += method2; return delegateChain.GetInvocationList() .Select(x => (WhatHappened) x.DynamicInvoke()) .Where( wh => (wh != WhatHappened.Nothing)) .ToList<WhatHappened>(); }
0 comments:
Post a Comment