首页 > 解决方案 > 为什么有时会收到此 NullReferenceException?

问题描述

这是一段有时会引发异常的代码:

Pendings = ClientCode.PendingOrders.Select(x => new DisplayPending()
{
    ItemCode = ClientCode.Items.First(y => y.Id == x.ItemCode).Id,
    ItemName = ClientCode.Items.First(y => y.Id == x.ItemCode).Name,
    OrderNo = x.BuyOrderNo == 0 ? x.SellOrderNo : x.BuyOrderNo,
    OrderType = x.OrderType == OrderType.Buy ? "Buy" : "Sell",
    PartyCode = x.PartyCode,
    Price = x.Price,
    Quantity = x.Quantity
});

这是消息:

System.NullReferenceException: 'Object reference not set to an instance of an object.'

x was null.

PendingOrders是一个ObservableCollection,它有 500 多个项目。

编辑

这是我填充扩展的方式ObservableCollection

public class AsyncObsetion<T> : ObservableCollection<T>
{
    SynchronizationContext context = SynchronizationContext.Current;
    readonly object _lock = new object();
    public AsyncObsetion() { BindingOperations.EnableCollectionSynchronization(this, _lock); }
    public AsyncObsetion(IEnumerable<T> list) : base(list) { BindingOperations.EnableCollectionSynchronization(this, _lock); }

    void RaiseCollectionChanged(object param) => base.OnCollectionChanged((NotifyCollectionChangedEventArgs)param);
    void RaisePropertyChanged(object param) => base.OnPropertyChanged((PropertyChangedEventArgs)param);

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (SynchronizationContext.Current == context) RaiseCollectionChanged(e);
        else context.Send(RaiseCollectionChanged, e);
    }

    protected override void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        if (SynchronizationContext.Current == context) RaisePropertyChanged(e);
        else context.Send(RaisePropertyChanged, e);
    }

    public void InsertRange(IEnumerable<T> items)
    {
        this.CheckReentrancy();
        foreach (var item in items)
            this.Items.Add(item);
        this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }
}

Client连接到 时Server,它会发送 6 个,这些 6 个byte[]被添加到 8个AsyncObsetion<T>名为ItemsNewsCollectionIssuedBuyOrdersSellOrdersPendingOrdersExecutedOrdersMyExecutedOrders客户端。这段代码负责处理byte[]客户端从服务器获取的代码:

var tasks = new List<Task>();
tasks.Add(Task.Run(() =>
{
    for (int i = 0; i < header.ItemSize; i += Constants.itemSize)
    {
        var item = PacMan<ItemStruct>.Unpack(itemArray.Skip(i).Take(Constants.itemSize).ToArray());
        Items.Add(new Item()
        {
            Id = item.Id,
            Name = item.Name,
            Cap = item.Cap,
            Floor = item.Floor,
            Mid = item.Floor + ((item.Cap - item.Floor) / 2),
            Securities = item.Securities,
            Owners = item.Owners,
            Govt = item.Govt,
            Institutions = item.Institutions,
            Foreign = item.Foreign,
            Public = item.Public,
            PerSecurity = PacMan<PerSecurityFinancialsStruct>.ArrayToList<PerSecurityFinancials>(item.PerSecurity),
            Dividends = PacMan<DividendStruct>.ArrayToList<Dividend>(item.Dividends),
            InitialSecurity = item.InitialSecurity
        });
    }
}).ContinueWith(t =>
{
    if (header.HasExecuted) GetExOrders(header.ExecutedSize, execArray);
    if (header.HasNews) AddNews(newsArray.ToArray());
}));

tasks.Add(Task.Run(() => { if (header.HasBuy) GetOrders(header.BuySize, buyArray, "buy"); }));
tasks.Add(Task.Run(() => { if (header.HasSell) GetOrders(header.SellSize, sellArray, "sell"); }));
tasks.Add(Task.Run(() => AddIssue(issueArray.ToArray())));
Task.WaitAll(tasks.ToArray());

hasReceivedData = true;
App.Current.Dispatcher.Invoke(() => CommandManager.InvalidateRequerySuggested());
OnConnected();
OrderVM.ItemCode = Items.First().Id;


e.Completed += Receive;
e.SetBuffer(headerBuffer, 0, headerBuffer.Length);
if (!e.AcceptSocket.ReceiveAsync(e)) Receive(null, e);

OnConnected()是一个事件,当它被触发时,客户端开始创建Pendingsout of的过程PendingOrders,该过程由buyArrayand组成sellArray。我认为这就是问题所在,我相信有时会以某种方式OnConnected()被解雇。Task.WaitAll(tasks.ToArray())如果有人感兴趣,这就是GetOrders

void GetOrders(int size, IEnumerable<byte> array, string type)
{
    var orderList = new List<AllOrder>();
    var pendingList = new List<AllOrder>();

    for (int i = 0; i < size; i += Constants.orderSize)
    {
        var order = PacMan<AllOrderStruct>.Unpack(array.Skip(i).Take(Constants.orderSize).ToArray());
        AddInitNewOrder(order, orderList, pendingList);
    }
    if (type == "buy") CheckNumberAndAdd(orderList, BuyOrders);
    else CheckNumberAndAdd(orderList, SellOrders);
    CheckNumberAndAdd(pendingList, PendingOrders);
}

这是AddInitNewOrder

void AddInitNewOrder(AllOrderStruct order, List<AllOrder> orders, List<AllOrder> pendingOrders)
{
    var o = new AllOrder();
    o.Action = order.Action;
    o.OrderType = order.OrderType;
    o.ItemCode = order.ItemCode;
    o.BrokerName = order.BrokerName;
    o.PartyCode = order.PartyCode;
    o.Price = order.Price;
    o.Quantity = order.Quantity;
    o.BuyOrderNo = order.BuyOrderNo;
    o.SellOrderNo = order.SellOrderNo;
    orders.Add(o);
    if (o.BrokerName == BrokerName) pendingOrders.Add(o);
}

这是CheckNumberAndAdd

void CheckNumberAndAdd<T>(List<T> normList, AsyncObsetion<T> obsList)
{
    var count = normList.Count;
    if(count > 50)
    {
        obsList.InsertRange(normList.Take(count - 50));
        var remaining = normList.Skip(count - 50).ToList();
        for (int i = 0; i < remaining.Count; i++) obsList.Insert(0, remaining[i]);                
    }
    else for (int i = 0; i < count; i++) obsList.Insert(0, normList[i]);                         
}

如果我在GetOrders函数中设置断点,我会看到pendingList从 483 个项目sellArray和从 494 个项目buyArray,所以我总共有 977 个项目。所以我应该总是得到 977 个项目,PendingOrders但是如果我删除GetOrders断点并在方法中设置断点Subscribe,该方法与该事件挂钩,ClientCode.OnConnected += Subscribe;我看到PendingOrders有时会得到少于 977 个项目。

标签: c#wpfobservablecollection

解决方案


解决该问题的一种方法是像这样包装最后三行GetOrders方法:

App.Current.Dispatcher.Invoke(() =>
{
    if (type == "buy") CheckNumberAndAdd(orderList, BuyOrders);
    else CheckNumberAndAdd(orderList, SellOrders);
    CheckNumberAndAdd(pendingList, PendingOrders);
});

这行得通。


推荐阅读