2012-04-11 15 views
5

Ho una domanda a cui non ho trovato risposta. Diciamo che abbiamo sia in Java o C# il seguente codice:Java ottimizzato per loop VS. Ciclo foreach .NET

class Car { 
    /* car stuff */ 
} 

E poi in Java

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

e C#

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

In C# i seguenti lavori bene:

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 

Questo funziona perché Truck è una sottoclasse di Car che in termini semplici significa che ogni autocarro è anche un'auto. Il problema è che il controllo dei tipi è fatto e solo i camion vengono selezionati da carList.

Se cerchiamo la stessa cosa in Java:

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

A causa del codice all'interno del ciclo maggiore, il codice non sarà nemmeno la compilazione. Invece dobbiamo fare qualcosa di simile per ottenere lo stesso effetto:

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

E 'la stessa idea, che in C# funziona senza alcun problema, ma in Java è necessario codice aggiuntivo. Anche la sintassi è quasi la stessa! C'è un motivo per cui non funziona in Java?

+1

Anche questo non è così pulito in C# poiché ogni Truck è una macchina, ma non ogni auto è un camion ... In C# la conversione è provato in fase di esecuzione, ma si dovrebbe essere veramente cauto con esso. Java sembra essere più cauto e vuole che il programmatore lo controlli prima (ovviamente la versione C# aggiunge qualche vantaggio se sai cosa stai facendo). –

+0

in C# viene controllato il tipo di ogni oggetto. Se è Truck, viene inserito il corpo del ciclo foreach, altrimenti non lo fa. Cosa non è pulito a riguardo?O lo controllo (Java) o il runtime lo controlla (C#) è lo stesso risultato finale. – alegen

+0

@alegen, non è vero. –

risposta

7

Il problema è che il controllo dei tipi è fatto e solo i camion vengono selezionati da carList.

No, non lo è. Se l'elenco contiene qualcosa tranne Truck s, si verificherà un'eccezione di runtime in C#. In sostanza, in C#, il seguente

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

si comporta come

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

La variante Java, d'altra parte, è di tipo-sicuro: Devi fare il cast e il tipo-controllo da soli.


Quindi, perché Java e C# si comportano in modo diverso? Questa è la mia ipotesi:

C# aveva la parola chiave foreach prima che generasse generici. Pertanto, non era possibile avere un List<Car>. Se C# aveva optato per il modo in cui Java di foreach, dovreste scrivere

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

che è fastidioso. D'altra parte, l'esteso per loop e generici sono stati introdotti in Java nella stessa versione (Java 5), ​​quindi non c'era bisogno di cast automatici.

+0

quindi intendi che se l'elenco non contiene alcun oggetto Truck, verrà generata un'eccezione? – alegen

+1

No, se c'è un oggetto non-truck, verrà lanciata un'eccezione –

+0

provandola e hai ragione ... dovresti rispolverare il mio C#, grazie per la tua risposta! – alegen

4

In C#, quando si scrive

foreach(Truck t in carList) 

ciò che il compilatore capisce è (più o meno):

var enumerator = carList.GetEnumerator(); 

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

L'assegnazione avrà esito negativo durante l'esecuzione se carlista contiene le automobili non camion.

si può provare la seguente applicazione console per illustrare:

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

Il C# e Java compilatori comportano in modo diverso qui perché scelte diverse sono state fatte durante la progettazione del linguaggio. Stranamente, "fallire il prima possibile, al momento della compilazione, se possibile" è una linea guida comune nelle specifiche C#. Qui è un caso in cui non è applicato e dove, per quanto mi piaccia il linguaggio C#, preferisco il comportamento java.

Tuttavia, in C#, LINQ offre un modo semplice per ottenere il functionnality si pensano che:

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

in questo modo l'enumerabile saranno filtrati e solo i camion renderà al loop.

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

Java Generics, introdotto in Java 5, è un po 'diverso dai generici di .NET.

Per farla breve, non è possibile scrivere codice come questo: si tratta di un errore in fase di compilazione. Si dispone di un elenco di vetture e si tenta di ottenere un elenco di camion - suona anche sbagliato/

L'approccio giusto è quello di utilizzare la classe Truck tramite la sua interfaccia Car. Se non puoi (non abbastanza metodi o metodi hanno un significato diverso) puoi filtrare la lista (prima di iterare) o .. ridisegnare le tue classi

1

Il programma non verrà compilato perché si utilizza la classe Truck al posto di Car.

Non c'è bisogno di questo

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

Si può fare in questo modo:

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

Sarà opere se si fa in questo modo:

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

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

O questo:

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

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

Invece di questo:

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

for(Truck truck : carList) { 
    // Do stuff here 
}