如何将基于 LINQ 的代码重写为 for 循环

本文关键字:重写 代码 for 循环 LINQ | 更新日期: 2023-09-27 18:35:32

我使用以下代码:

public class Card
{
  public Card(Rank rank, Suit suit)
  {
    this.Rank = rank;
    this.Suit = suit;
  }
  public Rank Rank { get; private set; }
  public Suit Suit { get; private set; }
}
public enum Rank : byte
{
  Ace   =  1,
  Two   =  2,
  Three =  3,
  Four  =  4,
  Five  =  5,
  Six   =  6,
  Seven =  7,
  Eight =  8,
  Nine  =  9,
  Ten   = 10,
  Jack  = 11,
  Queen = 12,
  King  = 13,
}
public enum Suit : byte
{
  Club    = 1, // Klavers
  Diamond = 2, // Ruiten
  Heart   = 3, // Harten
  Spades  = 4  // Schoppen
}
public class Deck 
{
    private List<Card> deck = new List<Card>(52);
    public Deck()
    {
        this.FillStack();
    }
    public void FillStack() 
    {
        this.deck.Clear();
        this.deck.AddRange(
          Enumerable.Range(1,4)
          .SelectMany( s =>
             Enumerable.Range(1, 13)
             .Select( n => new Card( (Rank)n , (Suit)s ) )
           )
        ) ;
    }

是否可以将 FillStack 中基于 LINQ 的代码替换为基于 for 循环的等效代码?目标是使代码更易于理解,并为不太熟悉 LINQ 的用户扩展。

如何将基于 LINQ 的代码重写为 for 循环

是的,这看起来是一个很好的示例,说明如何使用 LINQ 帮助程序方法。与普通的旧且更具可读

性相比,没有任何优势
for (int s = 1; s <= 4; s++)
  for (int n = 1; n <= 13; n++)
    this.deck.Add(new Card((Rank)n, (Suit)s));

不过,LINQ 版本可以提高可读性:下面是在不尝试将其全部填充到单个表达式中时的外观。

var suits = Enumerable.Range(1, 4);
var ranks = Enumerable.Range(1, 13);
var cards =
  from s in suits
  from n in ranks
  select new Card((Rank)n, (Suit)s);
this.deck.AddRange(cards);

这应该更容易理解,但它仍然意味着几乎相同的事情。

你可以这样说

public void FillStack()
{
  this.deck.Clear() ;
  this.deck.AddRange(
    Enumerable
    .Range(0,52)
    .Select( n => new Card( (Rank)(1+n%13), (Suit)(1+n/13) ) )
  ) ;
  return ;
}

甚至

public void FillStack()
{
  this.deck.Clear() ;
  foreach ( Rank rank in Enum.GetValues(typeof(Rank)) )
  {
    foreach( Suit suit in Enum.GetValues(typeof(Suit)) )
    {
      Card card = new Card( rank , suit ) ;
      this.deck.Add( card ) ;
    }
  }
  return;
}

您的问题是将 LINQ 查询替换为 for 循环,以便更容易理解。这很公平。但是,由于您正在学习,因此值得了解查询本身。然后,您将能够自己更换它,或者更好的是,您将对它感到足够舒适,可以将其留在那里。

让我们分解它并尝试逐行解释它:

[1]    this.deck
[2]            .AddRange(
[3]                Enumerable.Range(1, 4)
[4]                .SelectMany(s => 
[5]                    Enumerable.Range(1, 13)
[6]                    .Select(n => 
[7]                        new Card((Rank)n, (Suit)s))));

从 #3 行开始 Enumerable.Range(1,4) - 该方法返回从 14 的数字范围。所以我们有一个集合1, 2, 3, 4.在第 #4 行中,我们逐项访问此集合。因此,我们实际上迭代了这些项目。每个项目都由变量s表示 - 您可以使用它来访问项目。重复相同的机制以创建从 1 到 13 的迭代(行 #5 和 #6)。第二次迭代中的项由变量 n 访问。

更大的图景 - 我们从 1 迭代到 4,在内部我们从 1 迭代到 13。现在,在第 #7 行中,我们创建了一个Card,并且两次迭代的数字(表示为 ns)都用于类的构造函数中。因为我们遍历从 1 到 4 的数字,在 1 到 13 的内部,我们将得到所有的组合:(1,1) (1,2) (1,3) (1,4) (2,1) (2,2) 等等。组合中的每个项目都将创建一张卡片。因此,这将导致卡片的集合。现在回到第 #2 行AddRange该方法负责将此范围的卡片添加到deck集合中。

希望这将有助于理解查询并将其替换为更命令式的代码。