cannot reach my inner breakpoint - c#

I'm running the following code:
hashedUrlDataList.Select(mDalHashedUrlData.Save);
I have put a breakpoint in the called delegate,
public HashedUrlData Save(HashedUrlData item)
{
//breakpoint here
}
but it doesn't stop there.
How can I fix this?

Your method will be called when you'll enumerate the result of Select() not when declared.

Enumerable.Select is Lazy.
Try this and tell me if your break point is caught
hashedUrlDataList.Select(mDalHashedUrlData.Save).ToList();
Or the basic:
hashedUrlDataList.Select(mDalHashedUrlData.Save).GetEnumerator().MoveNext()
It just works if you have at least one element.
You can do it too:
hashedUrlDataList.Select(mDalHashedUrlData.Save).Any();
Any() do the same that GetEnumerator().MoveNext()
I think that what you want is:
List<HashedUrlData> hashedUrlDataList = new List<HashedUrlData>();
hashedUrlDataList.ForEach(Save);

LINQ is for querying data; it's not intended to cause side-effects.
If you're more interested in the side efects of your Savemethod than the HashedUrlData instance it returns, you should really be calling
foreach (HashedUrlData h in hashedUrlDataList)
{
h.Save();
}
If you will eventually be using the returned values and this is just an intermediate/debugging stage, then by all means use LINQ. Just be aware that Save will only be called as you access each returned value, or call something else that enumerates the whole enumerator as the other answers have shown.

Related

LINQ Select only throws IOException when the Enumerable is looked at

I'm currently using LINQ to load a list of files into XDocuments, like so:
var fileNames = new List<string>(){ "C:\file.xml" };
var xDocs = fileNames.Select(XDocument.Load);
var xDocs2 = xDocs.ToList(); // Crashes here
If I deliberately 'lock' one of the files with a different process, the IOException is only thrown when I actually start to look at the XDocuments I've been generating, ie when ToList() is called.
Can anyone explain why this is, and how best to handle this error? I'd like to have access to the working XDocuments still, if possible.
Can anyone explain why this is
As many pointed out, this is because of the so called deferred execution of many LINQ methods. For instanse, the Enumerable.Select method documentation states
This method is implemented by using deferred execution. The immediate return value is an object that stores all the information that is required to perform the action. The query represented by this method is not executed until the object is enumerated either by calling its GetEnumerator method directly or by using foreach in Visual C# or For Each in Visual Basic.
while the Enumerable.ToList documentation contains
The ToList<TSource>(IEnumerable<TSource>) method forces immediate query evaluation and returns a List that contains the query results. You can append this method to your query in order to obtain a cached copy of the query results.
So the XDocument.Load is really executed for each file name during the ToList call. I guess that covers the why part.
and how best to handle this error? I'd like to have access to the working XDocuments still, if possible.
I don't know what does "best" mean in this context, but if you want to ignore the errors and include the "working XDocuments", then you can use something like this
var xDocs = fileNames.Select(fileName =>
{
try { return XDocument.Load(fileName); }
catch { return null; }
});
and then either append .Where(doc => doc != null), or account for null documents when processing the list.
This is why the linq .Select is an IEnumerable and the elements are first called if you make your IEnumerable to an List. Then you go through all of your elements.

Using LINQ First() method shows "Possible multiple iteration of IEnumerable" warning by Resharper

I have code like this:
Person firstPerson = personsEnumerable.First();
, where personsEnumerable is IEnumerable<Person>
Now, Resharper underlines the personsEnumerable variable and says "Possible iteration of IEnumerable". I understand what this warning means from other questions here on SO, but I'm wondering why it's showing it in my example? I think First() returns the first element and there's no need to iterate the collection at all ?
Is this a 'general' warning message which is not applicable in my case (and I can ignore it) or I don't understand how First() actually works ?
Yes, it returns the first element but it still creates an enumerator to get that first element.You could add ToArray or ToList after Where to prevent this. However if you want to just get the first element, you could use the overloaded version of First which takes a Func delegate and you can remove the Where.
Consider this code:
var personsEnumerable = peopleList.OrderBy(p => p.Age);
var firstPerson = personsEnumerable.First();
foreach (var p in personsEnumerable)
{
// whatever
}
The first line of code sets things up to sort the list and create the results, but it doesn't actually do anything until you try to enumerate it. The second line of code, then would end up doing the sort. So although you're not enumerating the entire result, you're executing code to produce the result. And for a sort, that would entail doing all the heavy lifting.
The foreach loop would do the sort again before actually enumerating the results.

Is there any benefit to calling .Any() before .ForEach() when using linq?

I have many methods like the one below:
void ValidateBuyerRules()
{
var nodesWithRules = ActiveNodes.Where(x => x.RuleClass.IsNotNullOrEmpty());
**if (!nodesWithRules.Any()) return;**
foreach (var ruleClass in nodesWithRules)
{
// Do something here
}
}
As you can see, I check if nodesWithRules has any items and exit the method before conducting the foreach statement, but is this unecessary code?
Unless you have some logic after the foreach statement that you want to avoid, that's unnecessary as it will work the same.
When foreach iterates over nodesWithRules detects that there are no items and exit the loop.
If this is linq 2 sql, never do that.
You cause an extra round trip.
Also if you have any other type of IEnumerable, you should avoid that. .net does some tricks for underlying list, but you shouldn't rely on those.
There's really no point in calling Any before the Where. If no results come back from the Where query, you will never enter the for loop.
Calling the Any before the where will actually end up hurting performance because you're doing two queries.

IEnumerable with yield return and VS Quickwatch crash

I have the following method that return an IEnumerable
public IEnumerable<ExternalFilter> GetExternalFilters()
{
if (externalfilters == null)
yield break;
foreach (ExternalFilter filter in externalfilters)
yield return filter;
}
If I look at GetExternalFilter in VS Quickwatch, when expanding the collection, the in-debug program immediately crashes and Quickwatch shows nothing.
The same thing on evalutating Count() of that collection, with Quickwatch message Function evaluation was aborted.
What I need to have this functionality to work?
Just a hunch: maybe your enumeration externalfilter can only be evaluated once? (E.g. because of the source of the data).
Which means that as soon as you try to use QuickWatch to re-evaluate the result of GetExternalFilters it will fail.
If externalfilter is not just an enumerable but something like a simple List<T> then your problem lies somewhere else.
I believe that your parameter externalfilters is not thread safe.
Do you update your parameter from another thread?
If so, use lock to update list and reading from it.

Did I really just put a string data type into an IEnumerable<int>

Ok, this is a little weird. Ignore what I am trying to do, and look at the result of what happens in this situation.
The Code:
static string rawNumbers="1,4,6,20,21,22,30,34";
static IEnumerable<int> numbers = null;
static void Main(string[] args)
{
numbers = rawNumbers.Split(',').Cast<int>();
for (int i = 0; i < numbers.Count(); i++)
{
//do something
}
}
The Situation:
The line numbers = rawNumbers.Split(',').Cast<int>(); appears to work, and no exception is thrown. However, when I iterate over the collection, and InvalidCastException is thrown.
Now, drill down into the source and look at CastIterator<TResult>. This seems to be getting called at for (int i = 0; i < numbers.Count(); i++)...specifically numbers.Count()
static IEnumerable<TResult> CastIterator<TResult>(IEnumerable source) {
foreach (object obj in source) yield return (TResult)obj;
}
The error is happening on the cast, and when I look at the data in the source varaible it is a string[].
I would have thought that since the line where I call the cast to an int executed successfully everything was fine. What is going on behind the curtain? Is the string array really just being stored somewhere, and not being casted to T until it is called for? Lazy casting perhaps?
I know I cant do: (int)"42". My questions isn't how to make the cast work, but what is going on. Deferred execution of the cast? It seems weird the line where I call out Cast<int>() seems to work, but really doesn't.
A couple of things: first, you can't cast a string to an int. You can parse a string and get a resulting integer. But essentially you're trying to do this:
int x = (int)"32";
That's an exception. Second, the LINQ operators are deferred execution, so nothing actually happens until you attempt to iterate the result, that's when the real work starts happening.
With deferred execution there is no checking on whether the operations you're requesting are valid or can be properly performed, it just sets up the operations and then attempts to perform then individually upon iteration.
As has been noted, 'Deferred Execution' is the issue here. Many LINQ operators do not cause your lambda code to actually execute until you iterate over the result variable.
One reason for this is to allow you to build up a complex series of operations that can be executed at one time, rather than as a string of separate operations. This can be a useful thing - but it also can be tricky if you aren't expecting it.
The reason you are not getting an error immediately at the Cast<int>() line is because remember this is just chaining up the sequence of operations. Only once you iterate over the collection will the conversions be executed.
rawNumbers.Split(',') happens right away, but the Cast<int>() is deferred computation.
If you added a ToList() or ToArray() on that line, it would have executed immediately.
The reason that Cast appears to work, is because the IEnumerable isn't enumerated when the method is called, but rather when you call Count(). So, the call to Cast() really does nothing. The code fails as soon as the Cast is actually evaluated.
Instead of trying to cast, just do a simple conversion.
numbers = rawNumbers.Split(',').Select(str => int.parse(str));
I may be wrong but I wonder if it has to do with casting between int and string. I always get errors when I forget you cannot cast against string using (int), rather you have to use int.Parse. It probably has something to do with that but this is just off the top of my head.

Categories

Resources