为什么有时会收到此NullReferenceException? [英] Why do I get this NullReferenceException sometimes?

查看:106
本文介绍了为什么有时会收到此NullReferenceException?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Here's piece of code where It throws the exception sometimes:

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多个项目.

PendingOrders is anObservableCollection and It has more than 500 items.

编辑

这是我填充扩展的ObservableCollection的方式:

Here's how I populate my extended 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个byte[],这些6个byte[]被添加到名为ItemsNewsCollectionIssuedBuyOrdersSellOrdersAsyncObsetion<T>中>,ExecutedOrdersMyExecutedOrders在客户端.这段代码负责处理客户端从服务器获取的byte[]:

When Client connects to Server, it sends 6 byte[] which are added in 8 AsyncObsetion<T> named Items, NewsCollection, Issued, BuyOrders, SellOrders, PendingOrders, ExecutedOrders and MyExecutedOrders on client side. This piece of code is responible for handling those byte[] which client gets from server:

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()是一个事件,当它被解雇时,客户端开始从PendingOrders(由buyArraysellArray组成)中创建Pendings的过程.我认为这是问题所在,我相信OnConnected()有时会在Task.WaitAll(tasks.ToArray())之前被解雇.如果有人感兴趣,这是GetOrders的作用:

OnConnected() is an event and when it's fired client starts the process of creating Pendings out of PendingOrders, which is made out of buyArray and sellArray. I think here's the problem and I believe somehow OnConnected() gets fired before Task.WaitAll(tasks.ToArray()) sometimes. If someone is interested, here's what GetOrders does:

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函数中设置了brekpoint,我会看到pendingListsellArray中获得483个项目,从buyArray中获得494个项目,所以我总共有977个项目.因此,我应该总是在PendingOrders中获得977个项目,但是如果我在GetOrders中删除brekpoint并在Subscribe方法中设置了断点,则该事件已链接到该事件ClientCode.OnConnected += Subscribe;中,我看到PendingOrders有时会少于977个项目.

If I set brekpoint in GetOrders function, I see pendingList gets 483 items from sellArray and 494 items from buyArray, so altogether I've 977 items. So I should get 977 Items in PendingOrders always BUT If I remove brekpoint in GetOrders and set breakpoint in Subscribe method, which is hooked into that event ClientCode.OnConnected += Subscribe; I see PendingOrders sometimes gets less than 977 items.

推荐答案

正如@BionicCode所述,您在集合中仅包含一些null元素.

As @BionicCode stated, you just have some null elements in the collection.

只需使用.Where(x != null)或类似工具过滤掉它们即可.
尽管这可以解决问题,但通常未检测到的空引用是有关代码中星期数的提示.

Just filter them out with .Where(x != null) or similar.
Although this might solve the problem, generally undetected null references are a hint about week points in the code.

请考虑加强您的null检查策略,以免将来出现这些错误.使用相同的框架,您可以对可为空的对象执行检查,从而使代码更简洁,更健壮.

Consider reinforcing your null check policy to avoid these errors in the future. The same framework allows you to perform checks on nullable objects, making the code cleaner and more robust.

https://docs.microsoft.com/zh-cn/archive/msdn-magazine/2018/february/essential-net-csharp-8-0-and-nullable-reference-types

这篇关于为什么有时会收到此NullReferenceException?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆