If I correctly understand your application architecture, adding a few null-checks may solve your issue.
Your models:
public class ClassDetailOrder
{
public BinanceOrderBook Book { get; set; }
public string Symbol { get; set; }
}
public class BinanceOrderBook
{
public List<BinanceOrderBookEntry> Asks { get; set; }
public List<BinanceOrderBookEntry> Bids { get; set; }
}
public class BinanceOrderBookEntry
{
public decimal Price { get; set; }
public decimal Quantity { get; set; }
}
Your array of ClassDetailOrder
with some values (exampled):
private readonly ClassDetailOrder[] array = new ClassDetailOrder[]
{
// Item 0
null,
// Item 1
new ClassDetailOrder
{
Book = new BinanceOrderBook
{
Asks = new List<BinanceOrderBookEntry>()
{
new BinanceOrderBookEntry { Price = 50, Quantity = 5 }
}
},
Symbol = "book1"
},
// Item 2
new ClassDetailOrder
{
Book = new BinanceOrderBook
{
Bids = new List<BinanceOrderBookEntry>()
{
new BinanceOrderBookEntry { Price = 100, Quantity = 10 }
}
},
Symbol = "book2"
},
};
Calling GetBook
method with different parameters:
private void GetSomeBooks()
{
GetBook("Book0", 1, "asks", "");
GetBook(null, 2, "bids", "");
GetBook("Book2", 3, "asks", "");
GetBook("Book1", 4, null, "");
}
And your GetBook
method (with few my additions, changes and comments):
public decimal GetBook(string pair, decimal amount, string type, string operation, bool division = true)
{
// Default value
var result = -1.0M;
try
{
if (array is null)
{
// Handle in some way that "Array of ClassDetailOrders was null."
return result;
}
if (string.IsNullOrEmpty(pair))
{
// Handle in some way that "Pair value must be specified."
return result;
}
// Getting first Book from not-null(!) ClassDetailOrder,
// which Symbol matches to provided "pair" value
var book = array.FirstOrDefault(item => item?.Symbol == pair.ToLower())?.Book;
// Book will be null if:
// - all ClassDetailOrders in array was null
// - no ClassDetailOrder with some Symbol that match to pair.ToLower() was found
// - Book property of founded ClassDetailOrder was null by itself
// Checking Book is null or not and go out from method if null
if (book is null)
{
// Handle in some way that "Specified Book wasn't found."
return result;
}
// If Book wasn't null, initializing List
var lst = (List<BinanceOrderBookEntry>)null;
// Provided "type" value also may be null or empty
if (string.IsNullOrEmpty(type))
{
// Handle in some way that "Book Type must be specified."
return result;
}
switch (type)
{
case "asks":
lst = book.Asks;
break;
case "bids":
lst = book.Bids;
break;
}
// Same null-check as for Book above, but for List now
if (lst is null)
{
// Handle in some way that "Book items of provided Type was null.";
return result;
}
decimal[] arrayValue = new decimal[2] { 0M, 0M };
decimal orderPrice = 0, orderAmount = 0, totalCost = 0, totalAmount = 0, cost = 0;
decimal remaining = amount;
foreach (var item in lst)
{
// Same null-check as for Book and List above
if (item is null)
{
// Handle in some way that item was null, e.g.:
continue;
// or
return result;
}
orderPrice = item.Price;
orderAmount = item.Quantity;
cost = orderPrice * orderAmount;
remaining = cost < remaining ? remaining - cost : remaining - amount;
totalCost = cost < remaining ? totalCost + cost : amount * orderPrice;
totalAmount = cost < remaining ? totalAmount + orderAmount : totalAmount + amount;
// Reversed if because of ternary replacements
if (cost >= remaining)
{
arrayValue[0] = division
? Math.Round(amount / (totalCost / totalAmount), 8)
: Math.Round(totalCost / totalAmount * amount, 8);
arrayValue[1] = division
? Math.Round(amount / orderPrice, 8)
: Math.Round(orderPrice * amount, 8);
result = arrayValue[0];
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return result;
}
The main idea, that you should understand, is to check every nullable object for possible null value where you can. In this example we possibly may have 8 objects, which could be null
and may cause NullReferenceException
. And you can handle them all:
- For
array
of ClassDetailOrder
(to be able search for ClassDetailOrder
items in it);
- For provided
pair
value (to avoid null exception when lowercasing with ToLower()
);
- For each
ClassDetailOrder
(to avoid null exceptions while searching for Book
);
- For
ClassDetailOrder
at result (if no one was found by FirstOrDefault
method);
- For
Book
(to be sure that we can access it's Asks
/Bids
lists);
- For provided
type
value (to be sure be can take proper List
(Asks or Bids or any other you will have));
- For
List
(to be sure, that List
at least empty, but not null);
- For
BinanceOrderBookEntry
item in foreach
loop over List
(to be sure we can access its Price
and Quantity
properties).
You can handle null-checks in a way you wish or return value you wish or reverse if
s to is not null
(!= null
) way.
You can tests different values to try cover all null-checks and test them.