cho so với foreach so với LINQ


86

Khi tôi viết mã trong Visual Studio, ReSharper (Chúa phù hộ!) Thường đề nghị tôi thay đổi vòng lặp trường học cũ của mình dưới dạng foreach nhỏ gọn hơn.

Và thông thường, khi tôi chấp nhận thay đổi này, ReSharper tiến lên một bước và đề nghị tôi thay đổi lại, dưới dạng LINQ sáng bóng.

Vì vậy, tôi tự hỏi: có một số lợi thế thực sự , trong những cải tiến này? Trong quá trình thực thi mã khá đơn giản, tôi không thể thấy bất kỳ sự tăng tốc nào (rõ ràng), nhưng tôi có thể thấy mã ngày càng ít đọc hơn ... Vì vậy, tôi tự hỏi: nó có đáng không?


2
Chỉ cần một lưu ý - Cú pháp LINQ thực sự khá dễ đọc nếu bạn quen với cú pháp SQL. Ngoài ra còn có hai định dạng cho LINQ (các biểu thức lambda giống như SQL và các phương thức xích), có thể giúp bạn dễ học hơn. Nó chỉ có thể là những gợi ý của ReSharper khiến nó dường như không thể đọc được.
Shauna

3
Theo nguyên tắc thông thường, tôi thường sử dụng foreach trừ khi làm việc với một mảng có độ dài đã biết hoặc các trường hợp tương tự trong đó số lần lặp có liên quan. Đối với LINQ-ifying nó, tôi thường sẽ thấy ReSharper tạo ra một lời nói trước và nếu câu lệnh LINQ kết quả là gọn gàng / tầm thường / tôi có thể đọc được, và nếu không thì tôi sẽ hoàn nguyên nó. Nếu nó là một việc vặt để viết lại logic phi LINQ ban đầu nếu các yêu cầu thay đổi hoặc nếu có thể cần phải gỡ lỗi chi tiết thông qua logic thì câu lệnh LINQ đang trừu tượng hóa, tôi không LINQ nó và để nó lâu hình thức.
Ed Hastings

một lỗi phổ biến foreachlà xóa các mục khỏi bộ sưu tập trong khi liệt kê nó, trong đó thường cần một forvòng lặp để bắt đầu từ phần tử cuối cùng.
Slai

Bạn có thể nhận giá trị từ Øredev 2013 - Jessica Kerr - Nguyên tắc chức năng cho các nhà phát triển hướng đối tượng . Linq xuất hiện trong buổi thuyết trình ngay sau mốc 33 phút, dưới tiêu đề "Phong cách tuyên bố".
Theraot

Câu trả lời:


139

for so với foreach

Có một sự nhầm lẫn phổ biến là hai cấu trúc đó rất giống nhau và cả hai đều có thể hoán đổi cho nhau như thế này:

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

và:

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

Thực tế là cả hai từ khóa bắt đầu bằng ba chữ cái giống nhau không có nghĩa là về mặt ngữ nghĩa, chúng giống nhau. Sự nhầm lẫn này rất dễ bị lỗi, đặc biệt là cho người mới bắt đầu. Lặp lại thông qua một bộ sưu tập và làm một cái gì đó với các yếu tố được thực hiện với foreach; forkhông phải và không nên được sử dụng cho mục đích này , trừ khi bạn thực sự biết những gì bạn đang làm.

Hãy xem điều gì sai với nó bằng một ví dụ. Cuối cùng, bạn sẽ tìm thấy mã đầy đủ của một ứng dụng demo được sử dụng để thu thập kết quả.

Trong ví dụ này, chúng tôi đang tải một số dữ liệu từ cơ sở dữ liệu, chính xác hơn là các thành phố từ Adventure Works, được sắp xếp theo tên, trước khi gặp "Boston". Truy vấn SQL sau được sử dụng:

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

Dữ liệu được tải theo ListCities()phương thức trả về một IEnumerable<string>. Đây là những gì foreachtrông giống như:

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

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

Hãy viết lại bằng một for, giả sử rằng cả hai đều có thể hoán đổi cho nhau:

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

    Console.Write(city + " ");

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

Cả hai trở về cùng một thành phố, nhưng có một sự khác biệt rất lớn.

  • Khi sử dụng foreach, ListCities()được gọi một lần và mang lại 47 mục.
  • Khi sử dụng for, ListCities()được gọi là 94 lần và mang lại 28153 mặt hàng tổng thể.

Chuyện gì đã xảy ra?

IEnumerablelười biếng . Nó có nghĩa là nó sẽ thực hiện công việc chỉ vào lúc này khi cần kết quả. Đánh giá lười biếng là một khái niệm rất hữu ích, nhưng có một số lưu ý, bao gồm thực tế là rất dễ bỏ lỡ khoảnh khắc mà kết quả sẽ cần thiết, đặc biệt là trong trường hợp kết quả được sử dụng nhiều lần.

Trong trường hợp a foreach, kết quả chỉ được yêu cầu một lần. Trong trường hợp for như được thực hiện trong mã được viết không chính xác ở trên , kết quả được yêu cầu 94 lần , tức là 47 × 2:

  • Mỗi lần cities.Count()được gọi (47 lần),

  • Mỗi lần cities.ElementAt(i)được gọi (47 lần).

Truy vấn cơ sở dữ liệu 94 lần thay vì một cơ sở dữ liệu là khủng khiếp, nhưng không phải là điều tồi tệ hơn có thể xảy ra. Ví dụ, hãy tưởng tượng điều gì sẽ xảy ra nếu selecttruy vấn được đi trước bởi một truy vấn cũng chèn một hàng trong bảng. Đúng vậy, chúng ta sẽ có forcơ sở dữ liệu sẽ gọi cơ sở dữ liệu 2.147.483.647 lần, trừ khi nó hy vọng gặp sự cố trước đó.

Tất nhiên, mã của tôi là thiên vị. Tôi cố tình sử dụng sự lười biếng IEnumerablevà viết nó theo cách để liên tục gọi ListCities(). Người ta có thể lưu ý rằng một người mới bắt đầu sẽ không bao giờ làm điều đó, bởi vì:

  • Các IEnumerable<T>không có tài sản Count, nhưng chỉ có phương pháp Count(). Gọi một phương thức là đáng sợ, và người ta có thể mong đợi kết quả của nó không được lưu vào bộ nhớ cache và không phù hợp trong một for (; ...; )khối.

  • Việc lập chỉ mục không khả dụng IEnumerable<T>và không rõ ràng để tìm ElementAtphương thức mở rộng LINQ.

Có lẽ hầu hết những người mới bắt đầu sẽ chuyển đổi kết quả của ListCities()một thứ mà họ quen thuộc, như a 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;
    }
}

Tuy nhiên, mã này rất khác với foreachthay thế. Một lần nữa, nó cho kết quả tương tự, và lần này ListCities()phương thức chỉ được gọi một lần, nhưng mang lại 575 mục, trong khi với foreach, nó chỉ mang lại 47 mục.

Sự khác biệt đến từ thực tế là ToList()khiến tất cả dữ liệu được tải từ cơ sở dữ liệu. Mặc dù foreachchỉ yêu cầu các thành phố trước "Boston", nhưng forthành phố mới yêu cầu tất cả các thành phố phải được truy xuất và lưu trữ trong bộ nhớ. Với 575 chuỗi ngắn, có lẽ nó không tạo ra nhiều khác biệt, nhưng nếu chúng ta chỉ lấy được vài hàng từ một bảng chứa hàng tỷ bản ghi thì sao?

Vì vậy foreach, thực sự là gì?

foreachgần hơn với một vòng lặp while. Mã tôi đã sử dụng trước đây:

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

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

có thể được thay thế đơn giản bằng:

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

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

Cả hai đều sản xuất cùng một IL. Cả hai đều có kết quả như nhau. Cả hai đều có tác dụng phụ như nhau. Tất nhiên, điều này whilecó thể được viết lại trong một vô hạn tương tự for, nhưng nó sẽ còn dài hơn và dễ bị lỗi. Bạn được tự do chọn một trong những bạn thấy dễ đọc hơn.

Bạn muốn tự kiểm tra nó? Đây là mã đầy đủ:

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

Và kết quả:

--- cho ---
Abingdon Albany Alexandria Alhambra [...] Bonn Bordeaux Boston

Dữ liệu được gọi là 94 lần và mang lại 28153 mục.

--- với danh sách ---
Abingdon Albany Alexandria Alhambra [...] Bonn Bordeaux Boston

Dữ liệu được gọi là 1 lần và mang lại 575 mục.

--- trong khi ---
Abingdon Albany Alexandria Alhambra [...] Bonn Bordeaux Boston

Dữ liệu được gọi là 1 lần và mang lại 47 mục.

--- foreach ---
Abingdon Albany Alexandria Alhambra [...] Bonn Bordeaux Boston

Dữ liệu được gọi là 1 lần và mang lại 47 mục.

LINQ so với cách truyền thống

Đối với LINQ, bạn có thể muốn học lập trình chức năng (FP) - không phải công cụ C # FP, mà là ngôn ngữ FP thực như Haskell. Các ngôn ngữ chức năng có một cách cụ thể để thể hiện và trình bày mã. Trong một số tình huống, nó vượt trội hơn các mô hình phi chức năng.

FP được biết là vượt trội hơn nhiều khi nói đến việc thao túng danh sách ( danh sách là một thuật ngữ chung, không liên quan đến List<T>). Với thực tế này, khả năng thể hiện mã C # theo cách có chức năng hơn khi nói đến danh sách là một điều tốt.

Nếu bạn không bị thuyết phục, hãy so sánh khả năng đọc mã được viết theo cả hai cách chức năng và phi chức năng trong câu trả lời trước của tôi về chủ đề này.


1
Câu hỏi về ví dụ ListCities (). Tại sao nó chỉ chạy một lần? Tôi đã không gặp vấn đề gì khi nói về lợi nhuận trong quá khứ.
Dante

1
Anh ta không nói rằng bạn sẽ chỉ nhận được một kết quả từ IEnumerable - anh ta nói rằng truy vấn SQL (là phần đắt tiền của phương thức) sẽ chỉ thực hiện một lần - đây là một điều tốt. Sau đó nó sẽ đọc và mang lại tất cả các kết quả từ truy vấn.
HappyCat

9
@Giorgio: Trong khi câu hỏi này có thể hiểu được, việc có ngữ nghĩa của ngôn ngữ sẽ khiến người mới bắt đầu thấy khó hiểu sẽ không khiến chúng ta có một ngôn ngữ rất hiệu quả.
Steven Evers

4
LINQ không chỉ là đường ngữ nghĩa. Nó cung cấp thực hiện chậm trễ. Và trong trường hợp IQueryables (ví dụ Entity Framework) cho phép truy vấn được truyền và tạo cho đến khi nó được lặp lại (nghĩa là thêm một mệnh đề where vào một IQueryable được trả về sẽ dẫn đến SQL được chuyển đến máy chủ khi lặp đi lặp lại để bao gồm mệnh đề where giảm tải bộ lọc lên máy chủ).
Michael Brown

8
Nhiều như tôi thích câu trả lời này, tôi nghĩ rằng các ví dụ có phần bị chiếm đoạt. Tóm tắt ở cuối cho thấy rằng foreachhiệu quả hơn so với for, trong thực tế, sự chênh lệch là kết quả của việc cố tình phá mã. Tính kỹ lưỡng của câu trả lời tự cứu, nhưng thật dễ dàng để thấy một người quan sát bình thường có thể đưa ra kết luận sai như thế nào.
Robert Harvey

19

Trong khi có một số giải trình tuyệt vời đã có về sự khác biệt giữa for và foreach. Có một số sự hiểu sai về vai trò của LINQ.

Cú pháp LINQ không chỉ là cú pháp đường đưa ra một xấp xỉ lập trình chức năng cho C #. LINQ cung cấp các cấu trúc chức năng bao gồm tất cả các lợi ích của C #. Kết hợp với trả về IEnumerable thay vì IList, LINQ cung cấp thực thi hoãn lại của lần lặp. Những gì mọi người thường làm bây giờ là xây dựng và trả lại IList từ các chức năng của họ như vậy

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

Thay vào đó, hãy sử dụng cú pháp trả về lợi tức để tạo ra một phép liệt kê hoãn lại.

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

Bây giờ việc liệt kê sẽ không xảy ra cho đến khi bạn ToList hoặc lặp lại nó. Và nó chỉ xảy ra khi cần thiết (đây là một bảng liệt kê của Fftimeaci không có vấn đề tràn ngăn xếp)

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

Thực hiện một foreach qua hàm Fibonacci sẽ trả về chuỗi 46. Nếu bạn muốn thứ 30 đó sẽ là tất cả những gì sẽ được tính

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

Nơi chúng ta có nhiều niềm vui là sự hỗ trợ về ngôn ngữ cho các biểu thức lambda (kết hợp với các cấu trúc IQueryable và IQueryProvider, điều này cho phép cấu thành các truy vấn chức năng đối với nhiều tập dữ liệu khác nhau, IQueryProvider chịu trách nhiệm phiên dịch thông qua biểu thức và tạo và thực hiện một truy vấn bằng cách sử dụng các cấu trúc gốc của nguồn). Tôi sẽ không đi sâu vào các chi tiết khó chịu ở đây, nhưng có một loạt các bài đăng blog cho thấy cách tạo Nhà cung cấp truy vấn SQL ở đây

Tóm lại, bạn nên trả lại IEnumerable trên IList khi người tiêu dùng chức năng của bạn sẽ thực hiện một phép lặp đơn giản. Và sử dụng các khả năng của LINQ để trì hoãn việc thực hiện các truy vấn phức tạp cho đến khi chúng cần thiết.


13

nhưng tôi có thể thấy mã ngày càng ít đọc

Khả năng đọc là trong mắt của kẻ si tình. Một số người có thể nói

var common = list1.Intersect(list2);

là hoàn toàn có thể đọc được; những người khác có thể nói rằng điều này là mờ đục, và sẽ thích

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

như làm cho nó rõ ràng hơn những gì đang được thực hiện. Chúng tôi không thể cho bạn biết những gì bạn thấy dễ đọc hơn. Nhưng bạn có thể phát hiện ra một số sai lệch của riêng tôi trong ví dụ tôi đã xây dựng ở đây ...


28
Thành thật mà nói, Linq làm cho ý định khách quan dễ đọc hơn trong khi đối với các vòng lặp làm cho cơ chế khách quan dễ đọc hơn.
jk.

16
Tôi sẽ chạy nhanh nhất có thể từ một người nói với tôi rằng phiên bản for-for-if dễ đọc hơn phiên bản giao nhau.
Konamiman

3
@Konamiman - Điều đó sẽ phụ thuộc vào những gì một người tìm kiếm khi họ nghĩ về "khả năng đọc". bình luận của jk. minh họa điều này một cách hoàn hảo. Vòng lặp là dễ đọc hơn theo nghĩa là bạn có thể dễ dàng nhìn thấy như thế nào nó nhận được kết quả cuối cùng của nó, trong khi LINQ là dễ đọc hơn trong những gì kết quả cuối cùng nên.
Shauna

2
Đó là lý do tại sao vòng lặp đi vào thực hiện và sau đó bạn sử dụng Intersect ở mọi nơi.
R. Martinho Fernandes

8
@Shauna: Hãy tưởng tượng phiên bản for-loop bên trong một phương thức làm một số thứ khác; đó là một mớ hỗn độn. Vì vậy, một cách tự nhiên, bạn chia nó thành phương pháp riêng của nó. Dễ đọc, điều này giống như IEnumerable <T> .Intersect, nhưng bây giờ bạn đã sao chép chức năng khung và giới thiệu thêm mã để duy trì. Lý do duy nhất là nếu bạn cần triển khai tùy chỉnh vì lý do hành vi, nhưng chúng tôi chỉ nói về khả năng đọc ở đây.
Misko

7

Sự khác biệt giữa LINQ và foreachthực sự có hai phong cách lập trình khác nhau: mệnh lệnh và khai báo.

  • Bắt buộc: theo phong cách này, bạn nói với máy tính "làm điều này ... bây giờ làm điều này ... bây giờ làm điều này bây giờ làm điều này". Bạn cho nó ăn một chương trình một bước.

  • Tuyên bố: theo phong cách này, bạn nói với máy tính những gì bạn muốn kết quả đạt được và để nó tìm ra cách để đạt được điều đó.

Một ví dụ kinh điển của hai kiểu này là so sánh mã lắp ráp (hoặc C) với SQL. Trong hội đồng bạn đưa ra hướng dẫn (theo nghĩa đen) một lần. Trong SQL, bạn thể hiện cách nối dữ liệu với nhau và kết quả bạn muốn từ dữ liệu đó.

Một tác dụng phụ tốt đẹp của lập trình khai báo là nó có xu hướng cao hơn một chút. Điều này cho phép nền tảng phát triển bên dưới bạn mà không cần bạn phải thay đổi mã. Ví dụ:

var foo = bar.Distinct();

Có chuyện gì ở đây vậy? Là khác biệt sử dụng một lõi? Hai? Năm mươi? Chúng tôi không biết và chúng tôi không quan tâm. Các nhà phát triển .NET có thể viết lại bất cứ lúc nào, miễn là nó tiếp tục thực hiện cùng một mục đích, mã của chúng tôi có thể nhanh hơn một cách kỳ diệu sau khi cập nhật mã.

Đây là sức mạnh của lập trình chức năng. Và lý do mà bạn sẽ tìm thấy mã đó trong các ngôn ngữ như Clojure, F # và C # (được viết với tư duy lập trình chức năng) thường nhỏ hơn 3x-10 lần so với các đối tác bắt buộc.

Cuối cùng tôi thích kiểu khai báo vì trong C # hầu hết thời gian điều này cho phép tôi viết mã không làm thay đổi dữ liệu. Trong ví dụ trên, Distinct()không thay đổi thanh, nó trả về một bản sao mới của dữ liệu. Điều này có nghĩa là bất kể thanh là gì, và nó đến từ đâu, nó không đột nhiên thay đổi.

Vì vậy, giống như các áp phích khác đang nói, học lập trình chức năng. Nó sẽ thay đổi cuộc sống của bạn. Và nếu bạn có thể, hãy làm nó trong một ngôn ngữ lập trình chức năng thực sự. Tôi thích Clojure, nhưng F # và Haskell cũng là những lựa chọn tuyệt vời.


2
Xử lý LINQ được hoãn lại cho đến khi bạn thực sự lặp lại nó. var foo = bar.Distinct()về cơ bản là IEnumerator<T>cho đến khi bạn gọi .ToList()hoặc .ToArray(). Đó là một sự khác biệt quan trọng bởi vì nếu bạn không biết điều đó có thể dẫn đến các lỗi khó hiểu.
Berin Loritsch

-5

Các nhà phát triển khác trong nhóm có thể đọc LINQ không?

Nếu không thì đừng sử dụng nó hoặc một trong hai điều sẽ xảy ra:

  1. Mã của bạn sẽ không thể nhầm lẫn
  2. Bạn sẽ bị mắc kẹt với việc duy trì tất cả mã của bạn và mọi thứ dựa vào nó

Một vòng lặp cho mỗi vòng lặp là hoàn hảo để lặp qua một danh sách nhưng nếu đó không phải là những gì bạn phải làm thì đừng sử dụng một vòng lặp.


11
hmm, tôi đánh giá cao rằng đối với một dự án duy nhất thì đây có thể là câu trả lời, nhưng đối với trung hạn và dài hạn, bạn nên đào tạo nhân viên của mình, nếu không, bạn có một cuộc đua đến tận cùng của việc hiểu mã không có vẻ là một ý tưởng hay.
jk.

21
Trên thực tế, có một điều thứ ba có thể xảy ra: các nhà phát triển khác có thể bỏ ra một lượng nhỏ nỗ lực và thực sự học được điều gì đó mới và hữu ích. Nó không phải là chưa từng nghe thấy.
Eric King

6
@InvertedLlama nếu tôi ở một công ty nơi các nhà phát triển cần được đào tạo chính thức để hiểu các khái niệm ngôn ngữ mới thì tôi sẽ suy nghĩ về việc tìm kiếm một công ty mới.
Wyatt Barnett

13
Có lẽ bạn có thể thoát khỏi thái độ đó với các thư viện, nhưng khi nói đến các tính năng ngôn ngữ cốt lõi, điều đó không cắt giảm. Bạn có thể chọn và chọn khung. Nhưng một lập trình viên .NET giỏi cần phải hiểu từng tính năng của ngôn ngữ và nền tảng cốt lõi (Hệ thống. *). Và xem xét rằng bạn thậm chí không thể sử dụng EF đúng cách mà không sử dụng Linq, tôi phải nói rằng ... trong thời đại ngày nay, nếu bạn là một lập trình viên .NET và bạn không biết Linq, bạn không đủ năng lực.
Timothy Baldridge

7
Điều này đã có đủ downvote rồi, vì vậy tôi sẽ không thêm vào đó, nhưng một cuộc tranh luận ủng hộ đồng nghiệp không biết gì / không đủ năng lực không bao giờ là một điều hợp lệ.
Steven Evers
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.