属性 似乎在方法调用之间发生变化,但没有代码应该改变它

Property seems to be changing between method calls, but no code is supposed to be changing it

我有一个 Octet class 假设 "package" 八个样本,然后将它们发送出去。它具有添加新样本、检查样本是否已满以及从 Octet.

的八个值中提取 Frame 数据结构的方法。

Octetclass抛出两种异常:"cannot extract because not yet full"和"cannot add sample because already full"。为此,客户端代码应在调用 Add 之前检查是否已满,并在满时立即提取并重置它(老实说,这是一个非常蹩脚的 class 合同)。

问题是:我遇到了两种错误,尽管客户端 class - 唯一使用 Octet 的客户端 - 似乎在抛出的操作之前正确执行了检查, 但即使错误条件被击中。更糟糕的是,当我检查调试器中断时的值时,它们是正确的,也就是说,不应该抛出异常!

public class Client
{
    private Octet _octet = new Octet();

    void ProcessNewSamples(IEnumerable<int> newSamples)
    {
        foreach (int sample in newSamples)
        {
            if (!_octet.IsFull)
            {
                _octet.Add(sample);
            }

            if (_octet.IsFull)
            {
                var frame = _octet.ExtractFrame();
                this.SendElsewhere(frame);
                _octet.Reset();
            }

        }
    }
}


public class Octet
{
    const int ARRAY_SIZE = 8;
    int[] _samples = new int[ARRAY_SIZE];
    int _index = 0;

    public bool IsFull { get { return _index >= 8; } }

    public void Add(int sample)
    {
        if (IsFull)
        {
            throw new InvalidOperationException();
        }
        else
            _samples[_index++] = sample;
    }

    public Frame<int> ExtractFrame()
    {
        if (!IsFull)
            throw new InvalidOperationException();
        else
            return new Frame<int>(_samples);

    }

    public void Reset()
    {
        _samples = new int[ARRAY_SIZE];
        _index = 0;
    }
}

如评论中所述,如果您的函数被并行访问,则应加锁。

如果 SendElsewhere 很快,我会在函数周围加锁:

void ProcessNewSamples(IEnumerable<int> newSamples)
{
    lock (this)
    {
        foreach (int sample in newSamples)
        {
            if (!_octet.IsFull)
            {
                _octet.Add(sample);
            }

            if (_octet.IsFull)
            {
                var frame = _octet.ExtractFrame();
                this.SendElsewhere(frame);
                _octet.Reset();
            }
        }
    }
}

否则我会收集所有帧并在之后发送它们:

void ProcessNewSamples(IEnumerable<int> newSamples)
{
    var frames = new List<Frame>();

    lock (this)
    {
        foreach (int sample in newSamples)
        {
            if (!_octet.IsFull)
            {
                _octet.Add(sample);
            }

            if (_octet.IsFull)
            {
                var frame = _octet.ExtractFrame();
                frames.Add(frame);
                _octet.Reset();
            }
        }
    }

    foreach (var frame in frames)
    {
        this.SendElsewhere(frame)
    }
}