最近,我有一项任务是从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
检查,然后再次 - 这是执行任务的正确方法还是仅仅是幸运的情况下,我没有例外?
答案 0 :(得分:4)
accounts.FirstOrDefault()
为"GBP"
货币返回null。
然后在a.Amount
中使用此空值进行Where()
。我想你想要:
currencies.Select(currency => accounts.FirstOrDefault(x => x.Currency == currency))
.Where(a => a != null && a.Amount > 1) // <-- Added null check
.FirstOrDefault();
答案 1 :(得分:1)
正如Cameron所写,你的第一个FirstOrDefault可以返回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
}
或Aggregate方法:
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;
}