สำหรับเทียบกับ foreach กับ LINQ


86

เมื่อฉันเขียนโค้ดใน Visual Studio, ReSharper (ขอให้พระเจ้าคุ้มครองมัน!) มักจะแนะนำให้ฉันเปลี่ยนโรงเรียนเก่าของฉันสำหรับวงในรูปแบบ foreach ขนาดกะทัดรัดมากขึ้น

และบ่อยครั้งที่ฉันยอมรับการเปลี่ยนแปลงนี้ ReSharper ก้าวไปข้างหน้าและแนะนำให้ฉันเปลี่ยนอีกครั้งในรูปแบบ LINQ

ดังนั้นฉันสงสัยว่า: มีข้อได้เปรียบจริงบ้างไหมในการปรับปรุงเหล่านี้ ในการใช้งานโค้ดอย่างง่าย ๆ ฉันไม่เห็นการเพิ่มความเร็ว (ชัด) แต่ฉันเห็นโค้ดน้อยลงและอ่านง่ายขึ้น ... ดังนั้นฉันจึงสงสัยว่า: มันคุ้มหรือไม่


2
เพียงทราบ - ไวยากรณ์ LINQ สวยจริง ๆ อ่านถ้าคุณคุ้นเคยกับไวยากรณ์ SQL นอกจากนี้ยังมีสองรูปแบบสำหรับ LINQ (นิพจน์แลมบ์ดาที่เหมือน SQL และวิธีการโยง) ซึ่งอาจทำให้ง่ายต่อการเรียนรู้ อาจเป็นเพียงคำแนะนำของ ReSharper ที่ทำให้ดูเหมือนว่าอ่านไม่ได้
Shauna

3
โดยทั่วไปฉันมักใช้ foreach เว้นแต่ว่าจะทำงานกับอาร์เรย์ความยาวที่รู้จักหรือกรณีที่คล้ายกันซึ่งมีจำนวนการทำซ้ำที่เกี่ยวข้อง สำหรับ LINQ-ifying นั้นฉันมักจะเห็นสิ่งที่ ReSharper สร้างขึ้นมาสำหรับ foreach และถ้าคำสั่ง LINQ ที่ได้นั้นเป็นระเบียบเรียบร้อย / ไม่สำคัญ / อ่านง่ายฉันจะใช้มันและไม่เช่นนั้นฉันจะย้อนกลับ ถ้าเป็นงานที่ต้องเขียนตรรกะที่ไม่ใช่ LINQ ต้นฉบับอีกครั้งหากความต้องการเปลี่ยนไปหรือถ้ามันอาจจำเป็นในการดีบักอย่างละเอียดผ่านตรรกะที่คำสั่ง LINQ ย่อตัวลงฉันไม่ LINQ ทิ้งไว้นาน ฟอร์ม
Ed Hastings

ข้อผิดพลาดทั่วไปอย่างหนึ่งของforeachการลบรายการออกจากคอลเลกชันในขณะที่การแจงนับซึ่งโดยปกติจะต้องforวนรอบเพื่อเริ่มจากองค์ประกอบสุดท้าย
Slai

คุณอาจจะใช้ค่าจากØredev 2013 - เจสสิก้าเคอร์ - หลักการทำงานสำหรับนักพัฒนาเชิงวัตถุ Linq เข้ามาในงานนำเสนอหลังจากเครื่องหมาย 33 นาทีภายใต้ชื่อ "รูปแบบการประกาศ"
Theraot

คำตอบ:


139

for เมื่อเทียบกับ foreach

มีความสับสนร่วมกันว่าสิ่งก่อสร้างทั้งสองนั้นมีความคล้ายคลึงกันมากและทั้งสองสิ่งนี้สามารถแลกเปลี่ยนกันได้เช่นนี้

foreach (var c in collection)
{
    DoSomething(c);
}

และ:

for (var i = 0; i < collection.Count; i++)
{
    DoSomething(collection[i]);
}

ความจริงที่ว่าคำหลักทั้งสองเริ่มต้นด้วยตัวอักษรสามตัวที่เหมือนกันไม่ได้หมายความว่ามีความหมายเหมือนกัน ความสับสนนี้เป็นข้อผิดพลาดอย่างมากโดยเฉพาะอย่างยิ่งสำหรับผู้เริ่มต้น ทำซ้ำผ่านคอลเลกชันและการทำบางสิ่งบางอย่างที่มีองค์ประกอบที่จะทำกับforeach; forไม่จำเป็นและไม่ควรใช้เพื่อจุดประสงค์นี้เว้นแต่คุณจะรู้ว่าคุณกำลังทำอะไรอยู่

ลองดูว่ามีอะไรผิดปกติกับมันด้วยตัวอย่าง ในตอนท้ายคุณจะพบรหัสเต็มของแอปพลิเคชันตัวอย่างที่ใช้เพื่อรวบรวมผลลัพธ์

ในตัวอย่างเรากำลังโหลดข้อมูลบางส่วนจากฐานข้อมูลอย่างแม่นยำยิ่งขึ้นเมืองต่าง ๆ จาก Adventure Works เรียงตามชื่อก่อนที่จะพบ "Boston" ใช้แบบสอบถาม SQL ต่อไปนี้:

select distinct [City] from [Person].[Address] order by [City]

ข้อมูลจะถูกโหลดโดยวิธีการซึ่งส่งกลับListCities() IEnumerable<string>นี่คือสิ่งที่foreachดูเหมือนว่า:

foreach (var city in Program.ListCities())
{
    Console.Write(city + " ");

    if (city == "Boston")
    {
        break;
    }
}

ลองเขียนใหม่ด้วย a forโดยสมมติว่าทั้งคู่สามารถใช้แทนกันได้:

var cities = Program.ListCities();
for (var i = 0; i < cities.Count(); i++)
{
    var city = cities.ElementAt(i);

    Console.Write(city + " ");

    if (city == "Boston")
    {
        break;
    }
}

ทั้งสองกลับมาที่เมืองเดียวกัน แต่มีความแตกต่างกันมาก

  • เมื่อใช้foreach, ListCities()เรียกว่าหนึ่งครั้งและอัตราผลตอบแทน 47 รายการ
  • เมื่อใช้for, ListCities()เรียกว่า 94 ครั้งและผลตอบแทนถัวเฉลี่ย 28,153 รายการโดยรวม

เกิดอะไรขึ้น?

IEnumerableคือขี้เกียจ มันหมายความว่ามันจะทำงานเฉพาะในช่วงเวลาที่ต้องการผลลัพธ์ การประเมิน Lazy เป็นแนวคิดที่มีประโยชน์มาก แต่มีข้อควรระวังบางประการรวมถึงความจริงที่ว่าพลาดช่วงเวลาที่ต้องการผลลัพธ์โดยง่ายโดยเฉพาะอย่างยิ่งในกรณีที่ใช้ผลลัพธ์หลายครั้ง

ในกรณีของ a foreachผลลัพธ์จะถูกขอเพียงครั้งเดียว ในกรณีที่มีfor การนำมาใช้ในโค้ดที่เขียนไม่ถูกต้องด้านบนผลลัพธ์จะถูกร้องขอ 94 ครั้งคือ 47 × 2:

  • ทุกครั้งcities.Count()ถูกเรียก (47 ครั้ง)

  • ทุกครั้งcities.ElementAt(i)ถูกเรียก (47 ครั้ง)

การสืบค้นฐานข้อมูล 94 ครั้งแทนที่จะเป็นหนึ่งครั้งนั้นแย่มาก แต่ไม่ใช่สิ่งเลวร้ายที่อาจเกิดขึ้น ลองจินตนาการถึงตัวอย่างเช่นจะเกิดอะไรขึ้นหากselectแบบสอบถามจะนำหน้าด้วยคิวรีที่แทรกแถวในตารางด้วย ขวาเราจะมีforที่จะเรียกฐานข้อมูล2,147,483,647ครั้งเว้นแต่จะหวังว่าจะเกิดปัญหามาก่อน

แน่นอนรหัสของฉันลำเอียง ฉันจงใจใช้ความเกียจคร้านของและเขียนมันในทางที่จะเรียกซ้ำIEnumerable ListCities()เราสามารถทราบได้ว่าผู้เริ่มต้นจะไม่ทำเช่นนั้นเพราะ:

  • IEnumerable<T>ไม่ได้มีทรัพย์สินแต่เพียงวิธีการที่Count Count()การเรียกเมธอดน่ากลัวและใคร ๆ ก็สามารถคาดหวังได้ว่าผลลัพธ์จะไม่ถูกแคชและไม่เหมาะในfor (; ...; )บล็อก

  • ดัชนีไม่พร้อมใช้งานสำหรับIEnumerable<T>และก็ไม่ชัดเจนที่จะหาElementAtวิธีขยาย LINQ

อาจจะเริ่มต้นส่วนใหญ่ก็จะแปลงผลมาจากการที่จะสิ่งที่พวกเขามีความคุ้นเคยกับเช่นListCities()List<T>

var cities = Program.ListCities();
var flushedCities = cities.ToList();
for (var i = 0; i < flushedCities.Count; i++)
{
    var city = flushedCities[i];

    Console.Write(city + " ");

    if (city == "Boston")
    {
        break;
    }
}

ถึงกระนั้นรหัสนี้แตกต่างจากforeachทางเลือกมาก อีกครั้งมันให้ผลลัพธ์ที่เหมือนกันและในเวลานี้ListCities()วิธีการนั้นถูกเรียกเพียงครั้งเดียวแต่ให้ผลลัพธ์ 575 รายการในขณะที่ด้วยforeachจะให้ผลลัพธ์เพียง 47 รายการ

ความแตกต่างมาจากข้อเท็จจริงที่ToList()ทำให้ข้อมูลทั้งหมดถูกโหลดจากฐานข้อมูล ในขณะที่foreachขอเฉพาะเมืองก่อน "บอสตัน" เมืองใหม่forจะต้องเรียกคืนและจัดเก็บเมืองทั้งหมดในหน่วยความจำ ด้วยสตริงสั้น ๆ 575 มันอาจไม่ได้สร้างความแตกต่างมากนัก แต่ถ้าเราดึงข้อมูลเพียงไม่กี่แถวจากตารางที่มีเรคคอร์ดหลายพันล้านรายการ

ดังนั้นคืออะไรforeachจริง ๆ

foreachอยู่ใกล้กับลูปสักครู่ รหัสที่ฉันใช้ก่อนหน้านี้:

foreach (var city in Program.ListCities())
{
    Console.Write(city + " ");

    if (city == "Boston")
    {
        break;
    }
}

สามารถถูกแทนที่ด้วย:

using (var enumerator = Program.ListCities().GetEnumerator())
{
    while (enumerator.MoveNext())
    {
        var city = enumerator.Current;
        Console.Write(city + " ");

        if (city == "Boston")
        {
            break;
        }
    }
}

ทั้งสองผลิต IL เดียวกัน ทั้งคู่มีผลลัพธ์เหมือนกัน ทั้งสองมีผลข้างเคียงที่เหมือนกัน แน่นอนว่าสิ่งนี้whileสามารถเขียนใหม่ได้ในรูปแบบที่ไม่มีที่สิ้นสุดที่คล้ายกันforแต่อาจจะนานกว่าและมีข้อผิดพลาดได้ง่าย คุณมีอิสระที่จะเลือกสิ่งที่คุณอ่านได้มากขึ้น

ต้องการทดสอบด้วยตัวเอง? นี่คือรหัสเต็ม:

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Linq;

public class Program
{
    private static int countCalls;

    private static int countYieldReturns;

    public static void Main()
    {
        Program.DisplayStatistics("for", Program.UseFor);
        Program.DisplayStatistics("for with list", Program.UseForWithList);
        Program.DisplayStatistics("while", Program.UseWhile);
        Program.DisplayStatistics("foreach", Program.UseForEach);

        Console.WriteLine("Press any key to continue...");
        Console.ReadKey(true);
    }

    private static void DisplayStatistics(string name, Action action)
    {
        Console.WriteLine("--- " + name + " ---");

        Program.countCalls = 0;
        Program.countYieldReturns = 0;

        var measureTime = Stopwatch.StartNew();
        action();
        measureTime.Stop();

        Console.WriteLine();
        Console.WriteLine();
        Console.WriteLine("The data was called {0} time(s) and yielded {1} item(s) in {2} ms.", Program.countCalls, Program.countYieldReturns, measureTime.ElapsedMilliseconds);
        Console.WriteLine();
    }

    private static void UseFor()
    {
        var cities = Program.ListCities();
        for (var i = 0; i < cities.Count(); i++)
        {
            var city = cities.ElementAt(i);

            Console.Write(city + " ");

            if (city == "Boston")
            {
                break;
            }
        }
    }

    private static void UseForWithList()
    {
        var cities = Program.ListCities();
        var flushedCities = cities.ToList();
        for (var i = 0; i < flushedCities.Count; i++)
        {
            var city = flushedCities[i];

            Console.Write(city + " ");

            if (city == "Boston")
            {
                break;
            }
        }
    }

    private static void UseForEach()
    {
        foreach (var city in Program.ListCities())
        {
            Console.Write(city + " ");

            if (city == "Boston")
            {
                break;
            }
        }
    }

    private static void UseWhile()
    {
        using (var enumerator = Program.ListCities().GetEnumerator())
        {
            while (enumerator.MoveNext())
            {
                var city = enumerator.Current;
                Console.Write(city + " ");

                if (city == "Boston")
                {
                    break;
                }
            }
        }
    }

    private static IEnumerable<string> ListCities()
    {
        Program.countCalls++;
        using (var connection = new SqlConnection("Data Source=mframe;Initial Catalog=AdventureWorks;Integrated Security=True"))
        {
            connection.Open();

            using (var command = new SqlCommand("select distinct [City] from [Person].[Address] order by [City]", connection))
            {
                using (var reader = command.ExecuteReader(CommandBehavior.SingleResult))
                {
                    while (reader.Read())
                    {
                        Program.countYieldReturns++;
                        yield return reader["City"].ToString();
                    }
                }
            }
        }
    }
}

และผลลัพธ์:

--- สำหรับ ---
Abingdon Albany Alexandria Alhambra [... ] Bonn Bordeaux Boston

ข้อมูลนี้ถูกเรียกว่า 94 ครั้งและให้ผล 28153 รายการ

--- สำหรับรายชื่อ ---
Abingdon Albany Alexandria Alhambra [... ] Bonn Bordeaux Boston

ข้อมูลถูกเรียก 1 ครั้งและให้ผล 575 รายการ

--- ในขณะที่ ---
Abingdon Albany Alexandria Alhambra [... ] Bonn Bordeaux Boston

ข้อมูลถูกเรียก 1 ครั้งและให้ผล 47 รายการ

--- foreach ---
Abingdon Albany Alexandria Alhambra [... ] บอนน์บอร์โดซ์บอสตัน

ข้อมูลถูกเรียก 1 ครั้งและให้ผล 47 รายการ

LINQ กับวิธีดั้งเดิม

สำหรับ LINQ คุณอาจต้องการเรียนรู้ฟังก์ชั่นการเขียนโปรแกรม (FP) ไม่ใช่สิ่ง C # FP แต่ภาษา FP จริงเช่น Haskell ภาษาหน้าที่มีวิธีการเฉพาะในการแสดงและนำเสนอรหัส ในบางสถานการณ์มันจะดีกว่ากระบวนทัศน์ที่ไม่ทำงาน

FP เป็นที่รู้จักกันดีกว่ามากเมื่อพูดถึงการจัดการรายการต่างๆ ( รายการเป็นคำทั่วไปที่ไม่เกี่ยวข้องกับList<T>) จากข้อเท็จจริงนี้ความสามารถในการแสดงรหัส C # ในลักษณะที่ใช้งานได้มากกว่าเมื่อพูดถึงรายการต่าง ๆ เป็นสิ่งที่ดี

หากคุณไม่มั่นใจให้เปรียบเทียบความสามารถในการอ่านของโค้ดที่เขียนทั้งในรูปแบบการใช้งานและไม่ใช้งานในคำตอบก่อนหน้าของฉันในเรื่อง


1
คำถามเกี่ยวกับตัวอย่าง ListCities () ทำไมมันจะทำงานเพียงครั้งเดียว ฉันไม่เคยมีปัญหาในการหาผลตอบแทนในอดีต
Dante

1
เขาไม่ได้บอกว่าคุณจะได้รับหนึ่งผลลัพธ์จาก IEnumerable - เขาบอกว่าการสืบค้น SQL (ซึ่งเป็นส่วนที่แพงของวิธีการ) จะดำเนินการเพียงครั้งเดียวเท่านั้น - นี่เป็นสิ่งที่ดี จากนั้นจะอ่านและให้ผลลัพธ์ทั้งหมดจากแบบสอบถาม
HappyCat

9
@Giorgio: ในขณะที่คำถามนี้เข้าใจได้ แต่การมีความหมายของภาษาในสิ่งที่ผู้เริ่มต้นอาจรู้สึกสับสนจะไม่ทำให้เรามีภาษาที่มีประสิทธิภาพมาก
Steven Evers

4
LINQ ไม่ได้เป็นเพียงแค่ความหมายของน้ำตาล มันให้การดำเนินการล่าช้า และในกรณีของ IQueryables (เช่น Entity Framework) อนุญาตให้ส่งผ่านและสร้างแบบสอบถามได้จนกว่าจะมีการทำซ้ำ (หมายถึงการเพิ่มตำแหน่งในประโยคที่ส่งคืน IQueryable จะส่งผลให้ SQL ส่งผ่านไปยังเซิร์ฟเวอร์เมื่อมีการวนซ้ำ การลดการกรองลงบนเซิร์ฟเวอร์)
Michael Brown

8
มากเท่าที่ฉันชอบคำตอบนี้ฉันคิดว่าตัวอย่างมีการประดิษฐ์ค่อนข้าง บทสรุปในตอนท้ายแสดงให้เห็นว่าforeachมีประสิทธิภาพมากกว่าforเมื่อในความเป็นจริงความไม่เท่าเทียมกันนั้นเป็นผลมาจากการใช้รหัสผิดพลาดอย่างจงใจ ความละเอียดถี่ถ้วนของคำตอบจะไถ่ถอนตัวเอง แต่มันง่ายที่จะเห็นว่าผู้สังเกตการณ์แบบไม่เป็นทางการอาจสรุปได้ผิด
Robert Harvey

19

ในขณะที่มี expositions ที่ดีอยู่แล้วในความแตกต่างระหว่าง foreach และ มีการบิดเบือนความจริงบางประการเกี่ยวกับบทบาทของ LINQ

ไวยากรณ์ LINQ ไม่ได้เป็นเพียงแค่วากยสัมพันธ์ syntax ที่ให้ฟังก์ชั่นการโปรแกรมแบบประมาณกับ C # LINQ มอบโครงสร้างที่ใช้งานได้รวมถึงประโยชน์ทั้งหมดของ C # เมื่อรวมกับ IEnumerable ที่ส่งคืนแทน IList แล้ว LINQ จะให้การประมวลผลการทำซ้ำที่เลื่อนออกไป สิ่งที่คนทั่วไปทำตอนนี้คือสร้างและส่งคืน IList จากฟังก์ชันของพวกเขาเช่นนั้น

public IList<Foo> GetListOfFoo()
{
   var retVal=new List<Foo>();
   foreach(var foo in _myPrivateFooList)
   {
      if(foo.DistinguishingValue == check)
      {
         retVal.Add(foo);
      }
   }
   return retVal;
}

ใช้ไวยากรณ์ผลตอบแทนคืนแทนการสร้างการแจงนับ

public IEnumerable<Foo> GetEnumerationOfFoo()
{
   //no need to create an extra list
   //var retVal=new List<Foo>();
   foreach(var foo in _myPrivateFooList)
   {
      if(foo.DistinguishingValue == check)
      {
         //yield the match compiler handles the complexity
         yield return foo;
      }
   }
   //no need for returning a list
   //return retVal;
}

ตอนนี้การแจงนับจะไม่เกิดขึ้นจนกว่าคุณจะ ToList หรือวนซ้ำ และมันก็เกิดขึ้นได้ตามต้องการ (นี่คือการแจกแจงของ Fibbonaci ที่ไม่มีปัญหาล้นสแต็ค)

/**
Returns an IEnumerable of fibonacci sequence
**/
public IEnumerable<int> Fibonacci()
{
  int first, second = 1;
  yield return first;
  yield return second;
  //the 46th fibonacci number is the largest that
  //can be represented in 32 bits. 
  for (int i = 3; i < 47; i++)
  {
    int retVal = first+second;
    first=second;
    second=retVal;
    yield return retVal;
  }
}

การแสดงหน้าฟังก์ชั่นฟีโบนักชีจะส่งคืนลำดับ 46 หากคุณต้องการ 30 นั่นคือทั้งหมดที่จะถูกคำนวณ

var thirtiethFib=Fibonacci().Skip(29).Take(1);

ที่ที่เราได้รับความสนุกสนานมากมายคือการสนับสนุนในภาษาสำหรับการแสดงออกแลมบ์ดา (รวมกับการสร้าง IQueryable และ IQueryProvider สิ่งนี้ช่วยให้การจัดองค์ประกอบการสืบค้นของชุดข้อมูลกับชุดข้อมูลที่หลากหลาย IQueryProvider มีหน้าที่ในการตีความการส่งผ่าน การแสดงออกและการสร้างและการดำเนินการค้นหาโดยใช้การสร้างดั้งเดิมของแหล่งที่มา) ฉันจะไม่เข้าไปดูรายละเอียดเล็ก ๆ น้อย ๆ ที่นี่ แต่มีชุดของโพสต์บล็อกที่แสดงวิธีการสร้างผู้ให้บริการแบบสอบถาม SQL ที่นี่

โดยสรุปคุณควรกลับ IEnumerable มากกว่า IList เมื่อผู้บริโภคในฟังก์ชั่นของคุณจะทำซ้ำง่าย และใช้ความสามารถของ LINQ เพื่อเลื่อนการประมวลผลข้อความค้นหาที่ซับซ้อนจนกว่าจะมีความจำเป็น


13

แต่ฉันเห็นรหัสกลายเป็นน้อยลงและอ่านง่ายขึ้น

การอ่านอยู่ในสายตาของคนดู บางคนอาจพูดว่า

var common = list1.Intersect(list2);

สามารถอ่านได้อย่างสมบูรณ์; คนอื่นอาจบอกว่านี่เป็นทึบแสงและต้องการ

List<int> common = new List<int>();
for(int i1 = 0; i1 < list1.Count; i1++)
{
    for(int i2 = 0; i2 < list2.Count; i2++)
    {
        if (list1[i1] == list2[i2])
        {
            common.Add(i1);
            break;
        }
    }
}

ทำให้ชัดเจนยิ่งขึ้นว่ากำลังทำอะไรอยู่ เราไม่สามารถบอกคุณได้ว่าคุณอ่านอะไรได้มากขึ้น แต่คุณอาจตรวจพบอคติของฉันเองในตัวอย่างที่ฉันสร้างที่นี่ ...


28
สุจริตฉันจะบอกว่า Linq ทำให้ความตั้งใจที่อ่านง่ายขึ้นในขณะที่ลูปทำให้กลไกอ่านง่ายขึ้น
jk

16
ฉันจะทำงานให้เร็วที่สุดเท่าที่จะทำได้จากคนที่บอกฉันว่ารุ่น for-for-if สามารถอ่านได้มากกว่ารุ่นตัดกัน
Konamiman

3
@ Konamiman - ขึ้นอยู่กับสิ่งที่คนมองหาเมื่อพวกเขาคิดว่า "การอ่าน" ความคิดเห็นของ jk. แสดงให้เห็นอย่างสมบูรณ์แบบนี้ การวนซ้ำนั้นสามารถอ่านได้ง่ายขึ้นในแง่ที่ว่าคุณสามารถดูได้อย่างง่ายดายว่าการรับผลลัพธ์ที่ได้นั้นเป็นอย่างไรในขณะที่ LINQ สามารถอ่านได้มากขึ้นในสิ่งที่ผลลัพธ์ควรเป็น
Shauna

2
นั่นเป็นสาเหตุที่ลูปเข้าสู่การนำไปใช้งานและจากนั้นคุณใช้ Intersect ได้ทุกที่
R. Martinho Fernandes

8
@Shauna: ลองนึกภาพรุ่น for-loop ภายในวิธีการทำสิ่งอื่น ๆ มันเป็นระเบียบ ดังนั้นโดยธรรมชาติคุณแบ่งมันเป็นวิธีการของตัวเอง ความสามารถในการอ่านอย่างชาญฉลาดนี่เป็นเหมือนกับ IEnumerable <T> ตัดต่อ แต่ตอนนี้คุณได้ทำซ้ำฟังก์ชันการทำงานของเฟรมเวิร์กและแนะนำโค้ดเพิ่มเติมเพื่อรักษา ข้ออ้างเพียงข้อเดียวคือถ้าคุณต้องการการใช้งานที่กำหนดเองด้วยเหตุผลด้านพฤติกรรม แต่เรากำลังพูดถึงการอ่านได้ที่นี่เท่านั้น
Misko

7

ความแตกต่างระหว่าง LINQ และforeachboils ลงถึงสองสไตล์การเขียนโปรแกรมที่แตกต่างกัน: ความจำเป็นและการประกาศ

  • ความจำเป็น: ในรูปแบบนี้คุณจะบอกคอมพิวเตอร์ว่า "ทำสิ่งนี้ ... ทำสิ่งนี้ ... ตอนนี้ทำสิ่งนี้เดี๋ยวนี้" คุณฟีดโปรแกรมทีละขั้นตอน

  • คำแถลง: ในลักษณะนี้คุณจะบอกคอมพิวเตอร์ว่าคุณต้องการให้ผลลัพธ์เป็นอย่างไรและปล่อยให้มันหาวิธีไปที่นั่น

ตัวอย่างคลาสสิกของสองสไตล์นี้คือการเปรียบเทียบแอสเซมบลีโค้ด (หรือ C) กับ SQL ในการประชุมคุณให้คำแนะนำทีละตัวอักษร ใน SQL คุณจะแสดงวิธีเข้าร่วมข้อมูลร่วมกันและผลลัพธ์ที่คุณต้องการจากข้อมูลนั้น

ผลข้างเคียงที่ดีของการเขียนโปรแกรมที่ประกาศคือมันมีแนวโน้มที่จะสูงขึ้นเล็กน้อย สิ่งนี้ช่วยให้แพลตฟอร์มสามารถวิวัฒนาการภายใต้คุณได้โดยไม่ต้องเปลี่ยนรหัส ตัวอย่างเช่น

var foo = bar.Distinct();

เกิดอะไรขึ้นที่นี่ ความแตกต่างโดยใช้หนึ่งคอร์หรือไม่? สอง? ห้าสิบ? เราไม่รู้และไม่สนใจ .NET devs สามารถเขียนใหม่ได้ตลอดเวลาตราบใดที่ยังคงดำเนินการตามวัตถุประสงค์เดิมรหัสของเราก็สามารถทำงานได้เร็วขึ้นหลังจากการอัปเดตโค้ดอย่างน่าอัศจรรย์

นี่คือพลังของการเขียนโปรแกรมการทำงาน และเหตุผลที่คุณจะพบว่ารหัสในภาษาเช่น Clojure, F # และ C # (เขียนด้วยความคิดการเขียนโปรแกรมการทำงาน) มักจะมีขนาดเล็กลง 3x-10x กว่าคู่ที่จำเป็น

ในที่สุดฉันชอบสไตล์การประกาศเพราะใน C # ส่วนใหญ่เวลานี้ช่วยให้ฉันเขียนรหัสที่ไม่ได้กลายพันธุ์ข้อมูล ในตัวอย่างด้านบนDistinct()ไม่เปลี่ยนแถบมันจะส่งคืนสำเนาของข้อมูลใหม่ ซึ่งหมายความว่าแถบใดก็ตามและไม่เคยเปลี่ยนมาจากที่ไหน

เช่นเดียวกับโปสเตอร์อื่น ๆ ที่กำลังพูดถึง มันจะเปลี่ยนชีวิตคุณ และถ้าคุณสามารถทำได้ให้ใช้ภาษาโปรแกรมการทำงานจริง ฉันชอบ Clojure แต่ F # และ Haskell ก็เป็นตัวเลือกที่ยอดเยี่ยมเช่นกัน


2
การประมวลผล LINQ จะถูกเลื่อนออกไปจนกว่าคุณจะวนซ้ำจริง ๆ var foo = bar.Distinct()เป็นหลักIEnumerator<T>จนกว่าคุณจะโทรหรือ.ToList() .ToArray()นั่นคือความแตกต่างที่สำคัญเพราะถ้าคุณไม่ทราบว่ามันอาจนำไปสู่การยากที่จะเข้าใจข้อบกพร่อง
Berin Loritsch

-5

นักพัฒนาซอฟต์แวร์อื่น ๆ ในทีมสามารถอ่าน LINQ ได้หรือไม่

หากไม่ใช้อย่าใช้งานไม่เช่นนั้นหนึ่งในสองสิ่งจะเกิดขึ้น:

  1. รหัสของคุณจะไม่ถูกทำลาย
  2. คุณจะติดอยู่กับการบำรุงรักษารหัสทั้งหมดของคุณและทุกสิ่งที่อาศัยอยู่

A สำหรับแต่ละลูปนั้นเหมาะสำหรับการวนซ้ำในรายการ แต่ถ้านั่นไม่ใช่สิ่งที่คุณต้องทำอย่าใช้มัน


11
อืมฉันขอขอบคุณที่สำหรับโครงการเดียวนี่อาจเป็นคำตอบ แต่สำหรับระยะกลางถึงระยะยาวคุณควรฝึกอบรมพนักงานของคุณไม่เช่นนั้นคุณจะต้องแข่งกันที่ด้านล่างของความเข้าใจโค้ด
jk

21
อันที่จริงมีสิ่งที่สามที่อาจเกิดขึ้นได้: นักพัฒนารายอื่นสามารถใช้ความพยายามเพียงเล็กน้อยและเรียนรู้สิ่งใหม่และมีประโยชน์จริง ๆ มันไม่เคยได้ยินมาก่อน
Eric King

6
@InvertedLlama ถ้าฉันอยู่ใน บริษัท ที่ผู้พัฒนาต้องการฝึกอบรมอย่างเป็นทางการเพื่อทำความเข้าใจแนวคิดภาษาใหม่แล้วฉันจะคิดถึงการหา บริษัท ใหม่
ไวแอตต์บาร์เน็ตต์

13
บางทีคุณสามารถหลีกเลี่ยงทัศนคติที่มีต่อห้องสมุดได้ แต่เมื่อพูดถึงคุณลักษณะภาษาหลักแล้วนั่นก็ไม่ได้ลดทอนลง คุณสามารถเลือกและเลือกกรอบงาน แต่โปรแกรมเมอร์. NET ที่ดีจำเป็นต้องเข้าใจคุณลักษณะของภาษาแต่ละภาษาและแพลตฟอร์มหลัก (System. *) และเมื่อพิจารณาว่าคุณไม่สามารถใช้ภาษา EF ได้อย่างถูกต้องหากไม่ใช้ Linq ฉันต้องบอกว่า ... ในยุคนี้ถ้าคุณเป็นโปรแกรมเมอร์. NET และคุณไม่รู้ Linq คุณก็ไร้ความสามารถ
ทิโมธี Baldridge

7
นี่มี downvotes เพียงพออยู่แล้วดังนั้นฉันจะไม่เพิ่มลงในนั้น แต่ข้อโต้แย้งที่สนับสนุนผู้ร่วมงานที่ไม่รู้ / ไร้ความสามารถนั้นไม่ใช่สิ่งที่ถูกต้อง
Steven Evers
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.