从LINQ查询中获取null,即使我有匹配的结果
本文关键字:结果 查询 LINQ 获取 null | 更新日期: 2023-09-27 18:27:07
最近我有一项任务要从List<Account>
中选择一个帐户,条件是我有一个货币列表-List<string> currencies = new List<string> { "USD", "GBP", "EUR" };
,结果应该是第一个使用货币USD
的帐户,如果没有这样的帐户,则是一个使用currency == GBP
的帐户,最后是EUR
中的任何帐户。
因此,为了重现这个问题,我创建了一个简单的类Account
:
class Account
{
public string Currency { get; set; }
public string IBAN { get; set; }
public decimal Amount { get; set; }
}
然后我创建了两个实例:
列出帐户=新列表();
Account account = new Account();
account.Amount = 1;
account.Currency = "USD";
account.IBAN = "A1";
accounts.Add(account);
Account account1 = new Account();
account1.Amount = 2;
account1.Currency = "EUR";
account1.IBAN = "A2";
accounts.Add(account1);
然后我执行了这个查询:
var acc = currencies.Select(currency => accounts.FirstOrDefault(x => x.Currency == currency))
.Where(a => a.Amount > 1)
.FirstOrDefault();
结果(对我来说)出乎意料——一个错误说:
An unhandled exception of type 'System.NullReferenceException' occurred..
Additional information: Object reference not set to an instance of an object.
尽管有货币订单,但我确实需要一个额外的条件,所以我真的很困惑为什么会得到这个结果。如果我这样改的话:
var acc = currencies.Select(currency => accounts.FirstOrDefault(x => x.Currency == currency))
.Where(a => a != null && a.Amount > 1)
.FirstOrDefault();
然后我似乎得到了正确的行为,但首先我真的想知道为什么我必须进行null
检查,其次——这是执行任务的正确方式,还是我没有得到异常的幸运情况?
accounts.FirstOrDefault()
为"GBP"
货币返回null。
然后在Where()
中使用这个空值执行a.Amount
。我想你想要:
currencies.Select(currency => accounts.FirstOrDefault(x => x.Currency == currency))
.Where(a => a != null && a.Amount > 1) // <-- Added null check
.FirstOrDefault();
正如Cameron所写,您的第一个FirstOrDefault可能返回null并在链中引入null引用异常。
一种更可读的方法可能是使用字典:
string selectedCurrency =
currencies.FirstOrDefault(currency => accountsByCurrency.ContainsKey(currency));
if (selectedCurrency != null)
{
Account selectedAccount = accountsByCurrency[selectedCurrency];
// Do something with selected account
}
else
{
// No account was found
}
或者聚合方法:
var currencyRanks = new Dictionary<string, int>()
{
{ "USD", 1 },
{ "GBP", 2 },
{ "EUR", 3 }
};
int? workingRank = null;
Account selectedAccount = accounts.Aggregate((working, current) =>
{
int currentRank = 0;
if (currencyRanks.TryGetValue(current.Currency, out currentRank) &&
(currentRank < workingRank))
{
workingRank = currentRank;
return current;
}
return working;
};
请注意,这些都不允许在找到"美元"账户后立即部分迭代收款和保释。
如果你的收藏很大,而且你也想要这种行为,那么旧的时尚方式仍然可读:
private Account SelectAccountByCurrency(IEnumerable<Account> accounts)
{
Account result = null;
int? workingRank = null;
foreach (Account account in accounts)
{
int currentRank = 0;
if (currencyRanks.TryGetValue(account.Currency, out currentRank))
{
if (currentRank == 1)
{
return account;
}
else if (currentRank < workingRank)
{
result = account;
workingRank = currentRank;
}
}
return result;
}