2012-04-11 45 views
5

Tôi có một câu hỏi mà tôi không tìm thấy câu trả lời. Hãy nói rằng chúng tôi có trong hoặc java hay C# đoạn mã sau:Java tăng cường cho vòng lặp VS .NET foreach loop

class Car { 
    /* car stuff */ 
} 

Và sau đó trong Java

class Truck extends Car { 
    /* truck stuff */ 
} 

và C#

class Truck : Car { 
    /* truck stuff again */ 
} 

Trong C# các công việc sau tốt:

List<Car> carList = new List<Car>(); 
//add some objects to the collection 
foreach(Truck t in carList) 
    //do stuff with only the Truck objects in the carList collection 

Điều này có tác dụng vì Xe tải là một phân lớp của Xe, trong điều kiện đơn giản có nghĩa là mỗi Xe tải cũng là Xe hơi. Điều này là mặc dù, kiểm tra loại được thực hiện và chỉ có xe tải được chọn từ carList.

Nếu chúng ta cố gắng điều tương tự trong Java:

List<Car> carList = new ArrayList<Car>(); 
//add some objects to the collection 
for(Truck t : carList) 
    //**PROBLEM** 

Bởi vì các mã bên trong vòng lặp nâng cao, mã thậm chí sẽ không biên dịch. Thay vào đó chúng ta phải làm một cái gì đó như thế này để có được những tác dụng tương tự:

for(Car t : carList) 
    if(t instanceof Car) 
     //cast t to Truck and do truck stuff with it 

Đây là ý tưởng tương tự mà trong C# hoạt động mà không có vấn đề gì, nhưng trong Java bạn cần thêm mã. Ngay cả cú pháp gần như giống nhau! Có lý do nào khiến nó không hoạt động trong Java không?

+1

này cũng không phải là quá sạch bằng C# vì mỗi xe tải là một xe nhưng không phải mọi xe là một xe tải ... Trong C# chuyển đổi được cố gắng trong thời gian chạy, nhưng bạn nên thực sự thận trọng với nó. Java dường như thận trọng hơn về nó và muốn các lập trình viên kiểm tra nó trước tiên (tất nhiên phiên bản C# có thêm một số lợi ích nếu bạn biết những gì bạn đang làm). –

+0

trong C# loại của từng đối tượng được chọn. Nếu nó là Xe tải, cơ thể của vòng lặp foreach được nhập vào, nếu không nó sẽ không. Điều gì không sạch về điều đó?Hoặc tôi kiểm tra nó (Java) hoặc thời gian chạy kiểm tra nó (C#) nó 's cùng một kết quả cuối cùng. – alegen

+0

@alegen, điều đó không đúng. –

Trả lời

7

Mặc dù vậy, việc kiểm tra loại đó được thực hiện và chỉ có Xe tải được chọn từ carList.

Không, không phải vậy. Nếu danh sách của bạn chứa bất kỳ thứ gì trừ Truck s, ngoại lệ thời gian chạy sẽ xuất hiện trong C#. Về cơ bản, trong C#, sau

foreach(Truck t in carList) { 
    ... 
} 

cư xử như

foreach(object _t in carList) { 
    Truck t = (Truck)_t;  // throws an InvalidCastException if _t is not a Truck 
    ... 
} 

Các biến thể Java, mặt khác, là loại an toàn: Bạn phải làm cho các diễn viên và loại kiểm tra chính mình.


Vì vậy, tại sao Java và C# hoạt động khác? Đây là dự đoán của tôi:

C# có từ khóa foreach trước khi nó có dạng generics. Vì vậy, không có khả năng để có một List<Car>. Nếu C# đã chọn cách Java là foreach, bạn phải viết

foreach(object _c in myArraylistContainingOnlyCars) { 
    Car c = (Car)_c; 
    // do something with c 
} 

gây phiền toái. Mặt khác, mở rộng vòng lặp và generics đã được giới thiệu trong Java trong cùng một phiên bản (Java 5), ​​do đó, không cần cho phôi tự động.

+0

vì vậy bạn có nghĩa là nếu danh sách không có đối tượng Xe tải trong đó, một ngoại lệ sẽ được ném ra? – alegen

+1

Không, nếu có bất kỳ đối tượng phi xe tải nào trong đó, một ngoại lệ sẽ bị ném –

+0

để thử và bạn đúng ... nên chải lên C# của tôi, cảm ơn câu trả lời của bạn! – alegen

4

Trong C#, khi bạn viết

foreach(Truck t in carList) 

gì trình biên dịch hiểu là (xấp xỉ):

var enumerator = carList.GetEnumerator(); 

while (enumerator.MoveNext()) 
{ 
    Truck t = (Truck)enumerator.Current; 
    // do stuff 
} 

Việc chuyển nhượng sẽ thất bại khi chạy nếu carList chứa xe không tải.

Bạn có thể thử các ứng dụng giao diện điều khiển sau đây để minh họa:

namespace ConsoleApplication1 
{ 
    class Car 
    { 
    } 

    class Truck: Car 
    { 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      Car[] cars = new[] { new Car(), new Truck() }; 

      foreach (Truck t in cars) 
      { 
       Console.WriteLine("1 truck"); 
      } 

      Console.Read(); 
     } 
    } 
} 

C# và java biên dịch hành xử khác nhau ở đây vì sự lựa chọn khác nhau đã được thực hiện khi thiết kế ngôn ngữ. Thật kỳ lạ, "thất bại càng sớm càng tốt, tại thời gian biên dịch nếu có thể" là một hướng dẫn chung trong các thông số kỹ thuật C#. Đây là một trường hợp mà nó không được áp dụng và ở đâu, nhiều như tôi thích ngôn ngữ C#, tôi thích hành vi java.

Tuy nhiên, trong C#, LINQ cung cấp một cách dễ dàng để đạt được functionnality bạn nghĩ rằng nó có:

foreach (Truck t in carList.OfType<Truck>()) 
{ 
    //do stuff 
} 

rằng cách thức đếm được sẽ được lọc và chỉ có xe tải sẽ làm cho nó vào vòng lặp.

2
List<Car> carList = new ArrayList<Car>(); 
//add some objects to the collection 
for(Truck t : carList) 
    //**PROBLEM** 

Java Generics, được giới thiệu trong Java 5, hơi khác so với .NET Generics.

Câu chuyện dài ngắn - bạn không thể viết mã như thế này - đây là lỗi biên dịch. Bạn có một danh sách những chiếc xe và bạn cố gắng để có được một danh sách các Xe tải - nó thậm chí còn có vẻ sai/

Cách tiếp cận đúng là sử dụng lớp Truck qua giao diện Car của nó. Nếu bạn không thể (không đủ phương pháp hoặc phương pháp có ý nghĩa khác nhau) thì bạn có thể lọc danh sách (trước khi lặp lại) hoặc .. thiết kế lại các lớp học của bạn

1

Chương trình sẽ không được biên dịch vì bạn sử dụng lớp Xe tải thay vì Ô tô.

Không cần cho điều này một

for(Car t : carList) { 
    if(t instanceof Car) 
     // Do stuff here 
} 

Bạn có thể làm như thế này:

for(Car t : carList) { 
    // Do stuff here 
} 

Nó sẽ hoạt động nếu bạn làm như thế này:

List<Truck> truckList = new ArrayList<Truck>(); 

for(Car car : truckList) { 
    // Do stuff here 
} 

Hoặc này:

List<Car> carList = new ArrayList<Car>(); 

for (Iterator<Car> it = carList.iterator(); it.hasNext();) { 
    Truck car = (Truck) it.next(); 
    // Do stuff here 
} 

Thay vì điều này:

List<Car> carList = new ArrayList<Car>(); 

for(Truck truck : carList) { 
    // Do stuff here 
} 
Các vấn đề liên quan