3

I'd like to accomplish something like the following:

(clientSinceChoices =  Enumerable.Range(1949, DateTime.Now.Year - 1950)
    .Select(x => new SelectListItem() 
    { 
        Text = x != 1949 ? x.ToString() : "Unselected", 
        Value = x != 1949 ? new DateTime(x, 1, 1).ToString() : null,
        Selected = () => 
        {
            if (x == 1949 && !ClientSinceYearOnly.HasValue)
                return true;
            else if (ClientSinceYearOnly.Value == x)
                return true;
            else
                return false;
        }
    }));

I want the value of Selected to be the result of the labmda expression that is defined inline. I know I can accomplish this by assigning the lambda to a variable and then invoking it, but I think that defining and immediately invoking it is "cleaner".

2
  • 3
    Looking at the code, "clean" isn't one of the first things that comes to my mind ;P Commented Mar 17, 2012 at 14:23
  • @Saeb :( Yeah, you're right. It was cleaner before I added the case for 1949. Commented Mar 17, 2012 at 22:53

4 Answers 4

10

First off, to actually answer your question: you can invoke a lambda "in place" by casting it to a delegate:

bool x = ((Func<bool>) ()=>true) ();

But in your code there is no need for the lambda in the first place; I don't see why you have a lambda at all. You want to compute a Boolean, so compute the Boolean:

Selected = (x == 1949 && !ClientSinceYearOnly.HasValue) || 
                         (ClientSinceYearOnly.Value == x)

(Also, note that you do not have to check to see if the nullable has a value before you compare it; equality is "lifted to nullable" in C#.)

Second, this query is a mess because you've got a special case in there. I would not write your query like this in the first place. I would rather say:

var choices = new List<Item>();
choices.Add(new SelectListItem()
{
    Text = "Unselected",
    Value = null;
    Selected = ClientSinceYearsOnly == null
};

choices.AddRange(
    Enumerable.Range(1950, DateTime.Now.Year - 1951)
    .Select(x => new SelectListItem() 
    { 
        Text = x.ToString(), 
        Value = new DateTime(x, 1, 1).ToString(),
        Selected = x == ClientSinceYearOnly
    });

Much clearer. Or, write yourself an extension method that sticks something onto the start of a sequence:

public static IEnumerable<T> Prepend<T>(this IEnumerable<T> sequence, T first)
{
    yield return first;
    foreach(T item in sequence) 
        yield return item;
}

and then:

var choices = 
    Enumerable.Range(1950, DateTime.Now.Year - 1951)
    .Select(x => new SelectListItem() 
    { 
        Text = x.ToString(), 
        Value = new DateTime(x, 1, 1).ToString(),
        Selected = x == ClientSinceYearOnly
    })
    .Prepend(new SelectListItem()
    {
        Text = "Unselected",
        Value = null;
        Selected = ClientSinceYearsOnly == null
    });
Sign up to request clarification or add additional context in comments.

4 Comments

Thank you for the detailed answer. I'll be refactoring that code. Nifty idea with the Prepend; I thought about going that route before, but I didn't think of using yield. Why do you have to cast the lambda as a Func? Is it because the compiler doesn't know whether to treat it as a delegate or an expression without explicitly defining the type? Or am I missing the point there completely?
Your conjecture is correct; the compiler does not know what kind of delegate to convert the lambda to unless you tell it.
The "in place" lambda code does not compile for me. I'm targetting .NET 4.5 so I assume it should work. The compiler gives several errors such as "Invalid expression term ')'", Invalid expression term '=>', and 4 more.
Turns out I needed to add some parentheses to your expression to make the compiler happy: bool x = ((Func<bool>)(() => true ))();
5

You could always put the conditional in a method and invoke that:

public static bool ShouldBeSelected(int x)
{
    if (x == 1949 && !ClientSinceYearOnly.HasValue)
        return true;
    else if (ClientSinceYearOnly.Value == x)
        return true;
    else
        return false;
}

Then you can just use it:

        (clientSinceChoices =  Enumerable.Range(1949, DateTime.Now.Year - 1950)
                                        .Select(x => new SelectListItem() 
                                        { 
                                            Text = x != 1949 ? x.ToString() : "Unselected", 
                                            Value = x != 1949 ? new DateTime(x, 1, 1).ToString() : null,
                                            Selected = ShouldBeSelected(x)
                                        }));

Also, in your case, you could use a series of logical operators since you're returning a boolean (not recommended if you don't want the future maintainers to kill you in your sleep):

(x == 1949 && !ClientSinceYearOnly.HasValue) || (ClientSinceYearOnly.Value == x)

3 Comments

+1 exactly - the OP is getting caught up in shiny new toy disease.
@x0n maybe :( I did end up pulling it out into a method. So I guess it's not possible to assign Selected to the value of a lambda that's immediately invoked?
You probably could if you cast it to Func<bool> but that would be fugly. Selected = ((Func<bool>)() => { ... }).Invoke() -- oh, I jus saw someone else did that below.
1

This isn't the best use for a lambda, but the way you invoke a lambda inline is just by putting parentheses after it:

(clientSinceChoices =  Enumerable.Range(1949, DateTime.Now.Year - 1950)
    .Select(x => new SelectListItem() 
    { 
        Text = x != 1949 ? x.ToString() : "Unselected", 
        Value = x != 1949 ? new DateTime(x, 1, 1).ToString() : null,
        Selected = ((Func<bool>)(() => 
        {
            if (x == 1949 && !ClientSinceYearOnly.HasValue)
                return true;
            else if (ClientSinceYearOnly.Value == x)
                return true;
            else
                return false;
        }))() // <-- the () calls the function
    }));

Comments

0

The following code is functionally equivalent:

Selected = 
    x == 1949 && !ClientSinceYearOnly.HasValue ||
    ClientSinceYearOnly.Value == x;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.