views:

183

answers:

5

I'm having hard time understanding the following C# code. This code was taken from Pro ASP.NET MVC 2 Framework by Steven Sanderson. The code esentially creates URLs based on a list of categories.

Here's the code:

        Func<string, NavLink> makeLink = categoryName => new NavLink { 
            Text = categoryName ?? "Home", 
            RouteValues = new RouteValueDictionary(new { 
                controller = "Products", 
                action = "List", 
                category = categoryName, 
                page = 1 
            }),
            IsSelected = (categoryName == currentCategory)

A lot of stuff is going on here. I'm guessing it's defining a function that expects two parameters of type string, and NavLink. Then I see the Lambda categoryName => new NavLink etc.... I think all it's doing is creating an instance of NavLink.

The function is then called in the same Controller action:

        // Place home link on top
        List<NavLink> navLinks = new List<NavLink>();
        navLinks.Add(makeLink(null));

        // Add link for each distinct category
        var categories = productsRepository.Products.Select(x => x.Category.Name);
        foreach (string categoryName in categories.Distinct().OrderBy(x => x))
            navLinks.Add(makeLink(categoryName));

I can tell that it's making a list of NavLink. I don't understand why Steven Sanderson wrote it this way though. Couldn't he have written something like:

var categories = productsRepository.Products.Select(x => x.Category.Name);
foreach (string categoryName in categories.Distinct().OrderBy(x => x))
{
    var newlink = new Navlink{
        text = categoryName,
        RouteValues = new RouteValueDictionary(new {
           controller = "Products",
           action = "List",
           category = categoryName,
           page = 1
        }),
        IsSelected = (categoryName == currentCategory)
    }
    navLinks.Add(newlink);
}

Is there an advantage to doing it Steven's way versus my way?

+1  A: 

I'm guessing it's defining a function that expects two parameters of type string, and NavLink.

It take one parameter of string, and returns a NavLink.

The only advantage I see to creaing it that way is if you create NavLinks in multiple places. Then this way you have the means of creaing one all in one place --- the typical justification for a subroutine, which is pretty much all that is.

James Curran
Couldn't it be written like `NavLink makeLink (string categoryName) {etc..}`???
quakkels
@quakkels: Yes, it could. The only difference is that in his version, `makeLink` is locally scoped to the enclosing method.
Timwi
@quakkels: yes. I can see no really good reason for it being a delegate or a lambda, unless he uses it a different way elsewhere in the code.
James Curran
+5  A: 

It looks like that he wanted to use a local function that can be used for both adding the home link and as part of the categories loop. If he did it in-line like you did he would have to almost repeat the same code just for the single home link.

Mark Cidade
I didn't think about needing to repeat code for the home link.
quakkels
+1  A: 

It seems to be more about code style. I've also taken to using the more functional programming style when I became more comfortable with delegates.

Some advantages are (1) reuse of the function for future uses and (2) loops can be shortened -- it gets rather difficult to find matching braces when they're long and nested several levels.

Jerome
+1  A: 

(this is just opinion, not intended to offend or provoke)

Personally, I like your way better. (or a model where you just write a plain old private function that is called from within the foreach).

It's trendy to do things with lambda, but in this case you don't gain anything for it (there is no deferred execution going on). In this case, I think the lambda just makes the code a lot less clear.

JMarsch
I think it’s perfectly valid if you need the code only inside one method. A “plain old” private method would be accessible to the rest of the class, but the variable containing the lambda is accessible only to this method. Better encapsulation. You can still make it a private method if you need it somewhere else later.
Timwi
@Timwi: I understand your point, and I've even used a "private method" a couple of times -- Pascal allowed private methods for a long time. That said, it's only been a couple of times in about 18 years. The importance of encapsulation should not be underestimated, I agree, but but by the time you are talking about a private method in 1 class, you are pretty darned encapsulated -- neither inheritors nor outside forces can call that method, and your method gets first-class tooling support from object browsers, xml help, etc.
JMarsch
@JMarsch: ① Like I said, if you need tooling support, XML help, whatever, you can still make it a “normal” private method then. — ② I’ve only used WPF a couple of times in its existence, does that mean no-one else should use it?
Timwi
@Timwi 1: Just to be certain, I have searched this page, and I didn't see you talking about tooling support anywhere in your comments. 2: You aren't comparing apples to apples here, this post was discussing application of a technique. You are welcome to disagree, but there's no use getting up in arms about it.
JMarsch
@JMarsch: ① I said “You can still make it a private method later.” ② I’m highlighting why your argument doesn’t work. Just because you haven’t used it much doesn’t mean anyone else shouldn’t use it.
Timwi
@Timwi. This is getting old. I haven't said that you shouldn't have a private method -- I've even done it myself, as stated in my comments. I am saying that based on my own experience and in my opinion, it's rare that such usage is really warranted. I think quakkels's example constitutes code smell. I don't really understand why you seem to be so upset about this. If you want to do it, do it. Bill Gates won't hate you, satellites will remain in orbit. Old Faithful will keep right on erupting, and I will continue to think that code is less clear than just writing a method.
JMarsch
Upset is the wrong word, but yes, I generally take issue with dogmatism. A lambda expression does not, by itself, make the code harder to read, and it is not “unwarrented” or “code smell” just because it is a lambda. It is unwarrented when there is an actual issue with it, and no sooner. There is also no evidence that the author used it just because “it is trendy”. (By the way, you keep using the term “private method” when you seem to mean a lambda. It’s very confusing.)
Timwi
If disagreeing with you on this categorizes my opinion as dogmatic, then I stand convicted. I think that one follows or bucks convention based upon the path that yields the most desireable results. This usage offered no benefit, and if it had been no harder to interpret, it would likely not have been posted as a question. I characterized use of lambdas as trendy because I see them quite often in code in places where they offer no benefit (like this one). At no point in my post did I state that the author was trying to be trendy.
JMarsch
+2  A: 

Overuse of any advanced and cool construct just because it's advanced and cool usually doesn't result in anything but poor readability. Unless the lambda is used in some special sophisticated tricky way elsewhere, I would use a plain old private method instead...

Edit: It's worth adding that “special sophisticated tricky ways” shall be at least commented appropriately…

Ondrej Tucny