为什么有时会收到此NullReferenceException? [英] Why do I get this NullReferenceException sometimes?
问题描述
这是一段有时会引发异常的代码:
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[]
被添加到名为Items
,NewsCollection
,Issued
,BuyOrders
,SellOrders
,ExecutedOrders
和MyExecutedOrders
在客户端.这段代码负责处理客户端从服务器获取的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
(由buyArray
和sellArray
组成)中创建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,我会看到pendingList
从sellArray
中获得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.
这篇关于为什么有时会收到此NullReferenceException?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!