Java增强了for循环VS .NET foreach循环

ale*_*gen 6 c# java polymorphism foreach

我有一个问题,我没有找到答案.假设我们在java或c#中有以下代码:

class Car {
   /* car stuff */
}
Run Code Online (Sandbox Code Playgroud)

然后在Java中

class Truck extends Car {
   /* truck stuff */
}
Run Code Online (Sandbox Code Playgroud)

和C#

class Truck : Car {
   /* truck stuff again */
}
Run Code Online (Sandbox Code Playgroud)

在C#中,以下工作正常:

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
Run Code Online (Sandbox Code Playgroud)

这是因为卡车是汽车的子类,简单来说就意味着每辆卡车也是一辆汽车.但事情是,完成了类型检查,只从carList中选择了Trucks.

如果我们在Java中尝试相同的事情:

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

由于增强循环中的代码,代码甚至不会编译.相反,我们必须做这样的事情来获得相同的效果:

for(Car t : carList)
   if(t instanceof Car)
      //cast t to Truck and do truck stuff with it
Run Code Online (Sandbox Code Playgroud)

这与C#中没有任何问题的工作方式相同,但在Java中,您需要额外的代码.甚至语法几乎都一样!它有没有在Java中不起作用的原因?

Hei*_*nzi 9

但事情是,完成了类型检查,只从carList中选择了Trucks.

不,不是.如果您的列表包含除Trucks之外的任何内容,则C#中将发生运行时异常.基本上,在C#中,以下内容

foreach(Truck t in carList) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

表现得像

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

另一方面,Java变体是类型安全的:您必须自己进行强制转换和类型检查.


那么,为什么Java和C#的行为不同?这是我的猜测:

C#foreach在具有泛型之前有关键字.因此,没有可能有一个List<Car>.如果C#选择了Java方式foreach,你必须写

foreach(object _c in myArraylistContainingOnlyCars) {
    Car c = (Car)_c;
    // do something with c
}
Run Code Online (Sandbox Code Playgroud)

这很烦人.另一方面,扩展的for循环和泛型在Java中引入了相同的版本(Java 5),因此不需要自动强制转换.


Fal*_*nwe 5

在C#中,当你写的时候

foreach(Truck t in carList)
Run Code Online (Sandbox Code Playgroud)

编译器理解的是(粗略地):

var enumerator = carList.GetEnumerator();

while (enumerator.MoveNext())
{
    Truck t = (Truck)enumerator.Current;
    // do stuff
}
Run Code Online (Sandbox Code Playgroud)

如果carList包含非卡车车辆,则分配将在运行时失败.

您可以尝试以下控制台应用程序来说明:

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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

C#和java编译器在这里表现不同,因为在设计语言时已经做出了不同的选择.奇怪的是,"如果可能的话,尽快在编译时失败"是C#规范中的通用指南.这是一个没有应用它的情况,在我喜欢C#语言的地方,我更喜欢java行为.

但是,在C#中,linq提供了一种简单的方法来实现您认为具有的功能:

foreach (Truck t in carList.OfType<Truck>())
{
    //do stuff
}
Run Code Online (Sandbox Code Playgroud)

这样,可以过滤掉可枚举的东西,只有卡车才能进入循环.