繁体   English   中英

即使我有匹配的结果也从LINQ查询中获取null

[英]Getting null from a LINQ query even though I have matching result(s)

最近,我有一个任务要从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; }
}

然后,我创建了两个实例:

列表帐户=新的List();

        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;
};

请注意,所有这些都不允许对集合进行部分迭代并在找到“ USD”帐户后立即纾困。

如果您的收藏很大并且您也想要这种行为,那么仍然可以使用旧的流行方式:

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;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM