簡體   English   中英

再次延遲執行:LINQ 查詢/查詢中 Random 的本地實例

[英]Deferred execution again: Local instance of Random inside LINQ query/queries

我希望了解我今天偶然發現的以下行為。 這個小程序展示了“問題”:

class Bar
{
  public int ID { get; set; }
}

class Foo
{
  public Bar Bar { get; set; }
}

class Program
{
  private static IEnumerable<Bar> bars;
  private static IEnumerable<Foo> foos;

  static void Main(string[] args)
  {
    Random rng = new Random();
    bars = Enumerable.Range(1, 5).Select(i => new Bar { ID = i });
    foos = Enumerable.Range(1, 10).Select(i => new Foo
    {
      Bar = bars.First(b => b.ID == rng.Next(5) + 1)
    });
    var result = foos.ToList();
  }
}

此代碼不起作用; 它拋出一個InvalidOperationException說“序列不包含匹配的元素”。

但是,一旦我事先計算了隨機整數,它就會按預期工作:

var f = new List<Foo>();
for (int i = 0; i <= 20; i++)
{
  int r = rng.Next(5) + 1;
  f.Add(new Foo
  {
    Bar = bars.First(b => b.ID == r)
  });
}
foos = f;

正如標題所述,我懷疑延遲執行是造成這種情況的原因。 但是,如果有人能查明第一個代碼的問題並解釋其背后的確切原因,我會很高興。

(軼事: rng.Next(1) + 1確實有效,但我猜編譯器足夠聰明,可以用常量1替換它。)

問題是每次評估謂詞時都針對不同的 ID 進行測試。

foos ,您根本不需要foos 你可以只擁有:

var bar = bars.First(b => b.ID == rng.Next(5) + 1);

這將最多執行 5 次謂詞 - 每個bar元素一次 - 每次都會生成一個新的 ID 進行測試。 換句話說,它是這樣的:

public Bar FindRandomBarBroken(IEnumerable<Bar> bars, Random rng)
{
    foreach (var bar in bars)
    {
        if (bar.ID == rng.Next(bar.Count) + 1)
        {
            return bar;
        }
    }
    throw new Exception("I didn't get lucky");
}

而你想要的是:

public Bar FindRandomBarFixed(IEnumerable<Bar> bars, Random rng)
{
    // Only generate a single number!
    int targetId = rng.Next(bar.Count) + 1;
    foreach (var bar in bars)
    {
        if (bar.ID == targetId)
        {
            return bar;
        }
    }
    throw new Exception("Odd - expected there to be a match...");
}

您可以使用另一個Select調用來生成隨機 ID 來解決此問題:

foos = Enumerable.Range(1, 10)
                 .Select(_ => rng.Next(5) + 1)
                 .Select(id => new Foo { Bar = bars.First(b => b.ID == id) } );

請注意,由於對bars的延遲評估,您現在很可能會獲得多個具有相同 ID 的Bar實例 - 您可能需要在對bars賦值的末尾調用ToList()

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM