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

C# 从LINQ查询中获取null,即使我有匹配的结果,c#,linq,C#,Linq,最近,我有一项任务,从列表中选择一个账户,条件是我有一个货币列表-列表货币=新列表{“USD”、“GBP”、“EUR”}且结果应为第一个货币为美元的账户,如果没有此类账户,则为货币=英镑的账户,最后一个账户为欧元的账户 因此,为了重现这个问题,我创建了一个简单的类Account: class Account { public string Currency { get; set; } public string IBAN { get; set; } public deci

最近,我有一项任务,从
列表中选择一个账户,条件是我有一个货币列表-
列表货币=新列表{“USD”、“GBP”、“EUR”}且结果应为第一个货币为
美元的账户,如果没有此类账户,则为
货币=英镑的账户,最后一个账户为
欧元的账户

因此,为了重现这个问题,我创建了一个简单的类
Account

class Account
{
    public string Currency { get; set; }
    public string IBAN { get; set; }
    public decimal Amount { get; set; }
}
然后我创建了两个实例:

列表帐户=新列表()

然后我执行了这个查询:

var acc = currencies.Select(currency => accounts.FirstOrDefault(x => x.Currency == currency))
     .Where(a => a.Amount > 1)
     .FirstOrDefault();
结果是(对我来说)意外的结果-错误是:

发生“System.NullReferenceException”类型的未处理异常。。
附加信息:对象引用未设置为对象的实例。

我真的需要一个附加条件,尽管货币顺序不同,所以我真的很困惑为什么我会得到这个结果。如果我这样改变它:

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();
currency.Select(currency=>accounts.FirstOrDefault(x=>x.currency==currency))

。其中(a=>a!=null&&a.Amount>1)/
帐户。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();
currency.Select(currency=>accounts.FirstOrDefault(x=>x.currency==currency))

.Where(a=>a!=null&&a.Amount>1)/正如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;
};
var currencyRanks=new Dictionary()
{
{“美元”,1},
{“GBP”,2},
{“欧元”,3}
};
智力?workingRank=null;
Account selectedAccount=accounts.Aggregate((工作,当前)=>
{
int currentRank=0;
if(currencyRanks.TryGetValue(current.Currency,out currentRank)和
(当前等级<工作等级))
{
工作等级=当前等级;
回流;
}
返回工作;
};
请注意,这些都不允许在找到“美元”账户后立即部分重复收款和纾困

如果你的收藏很大,而且你也想要这种行为,那么旧的时尚方式仍然是可读的:

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;
}
私人账户选择AccountByCurrency(IEnumerable账户)
{
账户结果=空;
int?workingRank=null;
foreach(账户中的账户)
{
int currentRank=0;
if(currencyRanks.TryGetValue(account.Currency,out currentRank))
{        
如果(currentRank==1)
{
返回帐户;
}
else if(当前等级<工作等级)
{
结果=账户;
工作等级=当前等级;
}
}
返回结果;
}

正如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;
};
var currencyRanks=new Dictionary()
{
{“美元”,1},
{“GBP”,2},
{“欧元”,3}
};
int?workingRank=null;
Account selectedAccount=accounts.Aggregate((工作,当前)=>
{
int currentRank=0;
if(currencyRanks.TryGetValue(current.Currency,out currentRank)和
(当前等级<工作等级))
{
工作等级=当前等级;
回流;
}
返回工作;
};
请注意,这些都不允许在找到“美元”账户后立即部分重复收款和纾困

如果你的收藏很大,而且你也想要这种行为,那么旧的时尚方式仍然是可读的:

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;
}
私人账户选择AccountByCurrency(IEnumerable账户)
{
账户结果=空;
int?workingRank=null;
foreach(账户中的账户)
{
int currentRank=0;
if(currencyRanks.TryGetValue(account.Currency,out currentRank))
{        
如果(currentRank==1)
{
返回帐户;
}
else if(当前等级<工作等级)
{
结果=账户;
工作等级=当前等级;
}
}
返回结果;
}

@AlexK.对不起,输入错误。你能解释一下你认为“第一个或默认”中的“或默认”部分是什么意思吗?了解人们为什么会犯这样的错误有助于我设计更好的工具来捕捉或防止这些错误。@EricLippert我认为让我困惑的是,这段代码实际上已经运行了一段时间(显然,这只是一个幸运的巧合)因此,至少对我来说,我会说,令人困惑的部分是查询的执行顺序。我认为首先,这部分将被迭代
currency=>accounts.FirstOrDefault(x=>x.currency==currency)
直到找到匹配项。如果找不到匹配项,那么..好吧,我确实希望为null,但我想如果在货币检查中找不到匹配项,
LINQ
足够聪明,可以停止进一步执行。@EricLippert有没有一种使用LINQ的方法,在出现以下情况时,它不需要迭代整个集合:帐户使用美元作为货币?@vc:使用更好的数据结构,使对象已按货币排序(允许二进制搜索,如果“bucket”为-ized,甚至可以直接查找),或者添加索引并使用它。@AlexK.对不起,输入错误。您能解释一下您认为“first”或“default”中的“or default”部分是什么吗了解人们为什么会犯这样的错误有助于我设计更好的工具来捕捉或预防错误