Predicate inside a Where<> query not hit?

674 Views Asked by At

I need to call the method for each item in the list. So, I have used Where<> query as follows,

List<string> list = new List<string>();
list.Add("Name1");
list.Add("Name2");
list.Add("Name3");

var name = list.Where(n =>
{
    return CheckName(n);
});

But in the above case, CheckName() is not hit. The same method is triggered if I use FirstOrDefault<>. I don't know whether it is a framework break or I am going in a wrong way.

As additional info, I am using.NET Framework 4.5.

Has anyone experienced this error? If so, is there any solution to overcome this issue?

5

There are 5 best solutions below

5
Gilad Green On BEST ANSWER

You are understanding incorrectly the result of the Where condition. As linq is deffered executed it will only enter the where condition when materialized (by a ToList/FirstOrDefault/Sum and such).

The Where is never actually materialized in your current code (It did as you experienced when using FirstOrDefault) and as such it will never enter the CheckName method. Then, as Where will never return null but "worst case" an empty collection, which is not null, the result is true.

If you debug you will see that name equals true at the end of this. To "overcome" this depends on what is your desired output:

  1. If you want to know if you have any item that matched the predicate then:

    var result = list.Any(CheckName);
    
  2. If you want to retrieve those that match the predicate:

    var result = list.Where(CheckName);
    

    If later you want to query and check if results contains anything then:

    if(result.Any()) { /* ... */ }
    

    If you only want the results (and thus materializing the query):

    list.Where(CheckName).ToList();
    

Read more about linq being deffered executed here:


Just as a side note see how you can change your current code from:

var name = list.Where(n =>
{
    return CheckName(n);
})

To:

var name = list.Where(n => CheckName(n));

And eventually to:

var name = list.Where(CheckName);
0
Nikhil Agrawal On

LINQ has a Deferred Execution principal which means the query will not be executed until and unless you access name variable. If you want to execute it immediately, (just for example) add .ToList() in the end, which is exactly what FirstOrDefault does. It does immediate execution instead of deferred execution.

var name = list.Where(n =>
{
    return CheckName(n);
}).ToList() != null;

Also where condition result will never be null. Even if there is no object in list satisfying your condition(s) in CheckName, where will return an empty collection.

1
Habeeb On

The CheckName() method is not executed because of Deferred execution of Linq. The actual statement is not executed till you actually access it. So in your case, for the CheckName(), you should do something like:

var name = list.Where(n =>
{
    return CheckName(n);
}).ToList();
0
Caius Jard On

If you need to call a method for each item in a list then you should use a simple for loop:

foreach var name in list
  CheckName(name);

Just because LINQ is available, doesn't mean it should be used everywhere there is a collection. It is important to write code that makes sense and is self commenting and using it here has simultaneously introduced a flaw into your logic and made your code harder to read, understand and maintain. It's the wrong tool for the stated purpose

Doubtless you have additional requirements not stated here, like "I want to check every name in a list and make sure that none are null". You can and possibly should use linq for this but it looks more like

bool allNamesOK = list.All(n => n != null);

This code is compact and reads well; we can clearly see the intention (though I wouldn't call the list "list" - "names" would better)

0
Adwaenyth On

When you look at the Where-Method source code you can easily see why:

    internal static IEnumerable<T> Where<T>(this IEnumerable<T> enumerable, Func<T, bool> where) {
        foreach (T t in enumerable) {
            if (where(t)) {
                yield return t;
            }
        }
    }

The yield will cause the execution to only happen once the returned IEnumerable<T> is actually accessed. That is what is called deferred execution.