mdziadowiec
mdziadowiec

Reputation: 406

ArgumentOutOfRangeException despite boundary checks

I have created custom button in winforms in which i've added property with a list of custom class: List<Zasoby> and a method to add item to this list only when there is already item in that list that meets specific criteria (the lambda .where expression). The class Zasob is serializable. And in designer i add first Zasob to the list in this button like this:

bt01008xxx.Zasoby.Add(new Zasob { Lokalizacja = new Lokalizacja("01", "008", "000") });

..

public class ZasobSzczegolowoButton: Button, IAddZasoby
{

    private List<Zasob> _zasoby = new List<Zasob>(); //{ new Zasob { Lokalizacja = new Lokalizacja("01", "001", "000") } };


    [EditorBrowsable(EditorBrowsableState.Always)]
    [Browsable(true)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    [Bindable(true)]
    public List<Zasob> Zasoby
    {
        get { return _zasoby; }
        set
        {
            _zasoby = value;
            if (_zasoby.Any()) BackColor = _zasoby.Sum(x => x.Ilosc) > 0 ? Color.Coral : Color.White;
        }
    }

    public void AddZasoby(List<Zasob> zasoby)
    {
        var buton = Name;
        if (_zasoby != null && _zasoby.Count != 0)
        {
            var szukaneZasoby =
                zasoby?.Where(
                    x =>
                        x.Lokalizacja.ObszarKod == _zasoby[0].Lokalizacja.ObszarKod &&
                        x.Lokalizacja.Segment1 == _zasoby[0].Lokalizacja.Segment1);
            if (szukaneZasoby == null) return;

            Zasoby.Clear();
            Zasoby.AddRange(szukaneZasoby);
        }
    }
}

...

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Delegate,Inherited = false)]
[ComVisible(true)]
public class Zasob : Attribute
{
    public Towar Towar { get; set; }
    public Magazyn Magazyn { get; set; }
    public Lokalizacja Lokalizacja { get; set; }
    public decimal Ilosc { get; set; }
}

Now whenever i try to use method AddZasoby i get System.ArgumentOutOfRangeException. I check the list for null and count items and in debug mode and it shoud exit method but still somehow ends up in the body of the method with the error. (please see the screenshot below) Any idea what am i doing wrong?

1

Upvotes: 0

Views: 50

Answers (1)

Waescher
Waescher

Reputation: 5737

You check the list correctly for the count but with the code Zasoby.Clear(); you clear those elements from the member variable _zasoby again.

Don't let it trick yourself: You're defining the Where()-clause before you clear the list but it will be executed afterwards! That's the trick with lambdas, the predicates you wrote in your Where()-lambda will just be executed as soon as it is evaluated.

This feature is called Deferred Execution, see the first example here.

To fix that, you can enforce the lambda to be executed immediately by calling ToArray() or ToList() before you clear your list like:

  public void AddZasoby(List<Zasob> zasoby)
    {
        var buton = Name;
        if (_zasoby != null && _zasoby.Count != 0)
        {
            var szukaneZasoby =
                zasoby?.Where(
                    x =>
                        x.Lokalizacja.ObszarKod == _zasoby[0].Lokalizacja.ObszarKod &&
                        x.Lokalizacja.Segment1 == _zasoby[0].Lokalizacja.Segment1
                    ).ToList(); // *** NOTE ME HERE ***

            if (szukaneZasoby == null) return;

            Zasoby.Clear();
            Zasoby.AddRange(szukaneZasoby);
        }
    }

That should do the trick.

Upvotes: 2

Related Questions