查看原文
其他

挑战 Java 霸主之位?C# 五个不可替代的特性瞬间秒杀 Java

2017-10-28 苏宓 CSDN

点击上方“CSDN”,选择“置顶公众号”

关键时刻,第一时间送达!


完美的编程语言并不存在,相信这一点在众多开发者的心中毋庸置疑。想要学会一门编程语言就走遍天下,至少目前看来并不现实。一门新语言的诞生往往是为了克服另一种语言的弊端,但在保证新语言健壮性的同时却似乎又不可避免在另一方面出现不足。


纵观大小 600 多种编程语言,我们究竟该如何选择适合自己的开发语言?是根据兴趣、工作需求、还是课程的推荐安排?笔者曾是软件工程专业,学习了 Java、C 语言、C++、C# 等多门编程语言,最终却也是略懂而不精。事实上,编程语言并非学得越多就越牛,术业有专攻,要掌握好适合自己的开发利器。本文以全世界通用的 C#、Java 两门基础编程语言为例,综合部分经验丰富的开发者的看法,分享该编程语言的一些特性,希望能给迷茫中的开发者指引一定的方向。


C# 与 Java 的不同之处


对于已有经验的技术工程师而言,往往不会去纠结这个问题,但是在新入门的开发者看来,C# 与 Java 往往代表的是两大流派。其实 C# 和 Java 都起源于 C/C++ 语言,它们在面向对象方面有许多相似之处。除了 Java JVM 和 C# .NET CLR 有许多相同结构上的相似性之外,它们各自的开发团队都有各自的发展方向。


倘若从语言的角度上来讲,C# 似乎更胜一筹,因为它易用、门槛低、优雅、较为简洁,且 C# 出现的时间比 Java 晚,因此自然吸收、借鉴,同时又有它的创新,比如很早就支持 Lamda 表达式、event 和委托、var 及 LINQ。


如果从面向接口来讲,Java 同样可以做到 event 和 listener,只不过对象引用的传递比较直接。


如果从应用的角度来看,以 Java 和 C# 为编程语言自成的体系几乎都涵盖了主流的开发方向:


桌面、Web、服务端、数据库、网络、移动端、中间件。


而两者最大的不同之处在于:


  • 泛型不一样,Java 泛型只是编译时的,但 C# 的泛型在运行时也被维持,而且适用于 value types 和 reference types;

  • C# 没有 checked exceptions;

  • Java 不允许建立user-defined 的value types;

  • Java 不允许运算符重载;

  • Java 没有类似 LINQ 的特性;

  • Java 不支持委托;

  • C# 没有匿名内部类;

  • C# 没有像 Java 那样的内部类,所有的 nested classes 其实都像 Java 的静态 nested classses;

  • Java 没有静态类;

  • Java 没有扩展方法(extension methods);

  • 两者的访问修饰符有一定区别;

  • 两者初始化(initialization)的顺序有一定不同。C#初始化变量后才调用父类的构造方法

  • Java 没有类似“properties”的东西,而是约定俗成为 getter 和 setter;

  • Java 没有类似与"unsafe"的特性;

  • 两者的枚举(enums)有一定的不同,Java 的更加面向对象。


注:以上只是简单列举,欢迎大家补充。


C# 五个不可替代的特性瞬间秒杀 Java 


编程语言各有千秋,如果我们可以同时拥有 C# 和 Java 最好的特性,那会是什么样呢?接下来,将罗列出 C# 开发者能用到而 Java 中没有的那些特性。


1. LINQ


LINQ (Language-Integrated Query,语言集成查询) 于 2007 年引入到 C#,以帮助开发人员从各种数据源查询数据。使用它,我们可以在无需考虑正在调用的特定数据库的语法来编写查询语句。LINQ provider 所提供的一个组件将查询转换为下层数据源可读的格式。例如,如果我们需要从 SQL 数据库查询数据,LINQ to SQL provider 程序将把 LINQ 查询转换成 T-SQL,以便数据库可以理解它。


要在 LINQ 中执行查询操作,首先获取数据库,然后创建查询,最后执行查询。在 LINQ to Object 查询中,这可能仅像一样代码一样简单,而不是为每个循环编写嵌套的复杂迭代。

例如,我们来看看这个代码,用于在 C# 中从列表中过滤 2 位数。


首先,在不使用 LINQ 的情况下:


List<int> FilterTwoDigitNumbersWithoutLinq(List<int> numbers)

{

    var tens = new List<int>();

    

    for (var i=0; i < numbers.Count(); i++)

    {

        if ((9 < numbers[i]) && (numbers[i] < 100))

        {

            tens.Add(numbers[i]);

        }

    }

    

    return tens;

}


如果使用 LINQ 查询语法形式:


List<int> FilterTwoDigitNumbersWithLinq(List<int> numbers)

{

    return (from a in numbers

            where (a > 9 && a < 100)

            select a).ToList();

}


或者是方法语法形式:


List<int> FilterNonTwoDigitNumbersWithLinq2(List<int> numbers)

{

    return numbers.Where(a => a > 9 && a < 100).ToList();

}


这里两种语法都是正确的,唯一的区别就是查询语法看起来更像是 SQL 语句而方法语法使用 lambda 表达式(当然,看起来很像我们在 Java 里写的某些代码)


综述:LINQ 所依赖的许多特性,如 lambda 表达式(就 LINQ 来说非常有用),已经在 Java 中有了等效的实现,尽管我们可以使用流和 lambda 来查询数据,但 LINQ 简化了整个过程并且移除了很多在 Java 中存在的冗余代码。


2. Struct


C# 中的结构体类似于类。实际上,一个 struct 甚至可以被认为是一个“轻量级类”,因为它可以包含构造函数、常量、方法等等。一个结构体和一个类之间最大的区别在于结构是值类型,而类是引用类型。


相比于创建类,编写结构体最重要的好处是在构造一个值类型时比在构造引用类型时更容易确保值语义。如 Microsoft 的文档所述,“struct 类型的变量直接包含结构体的数据,而类类型的变量包含对数据的引用。”因此,对比使用类时,使用结构体的好处之一是,从代码的其他部分更改其值的唯一方法是将其作为参考进行显式传递。


微软的开发人员建议对于那些小于 16 字节、生命周期短、不改变的而且不常装箱的类型,使用结构体(struct)而不是类(class)。在这种情况下,使用结构体可能会比使用类更有效率,因为它会保存在栈而不是堆中。


比如:


public struct Point

    {

        public int X;

        public int Y;


        public Point(int X, int Y)

        {

            this.X = X;

            this.Y = Y;

        }


        public static Point operator +(Point p1, Point p2)

        {

            return new Point(p1.X + p2.X, p1.Y + p2.Y);

        }


        public override string ToString()

        {

            return ($"({X}, {Y})");

        }

    }


    class Program

    {

        static void Main(string[] args)

        {

            Point point1 = new Point(1, 5);

            Point point2 = new Point(2, 3);


            Console.WriteLine("The addition of both points will result in: {0}", (point1 + point2));


            Console.ReadKey();

        }

}


小结:很多情况下使用结构体可以节省内存分配和释放的时间,这确实很有吸引力。然而事实是值类型拥有自己的存储空间。无论结构体拥有如何明显的优点和缺点,这在 Java 中都不需要操心。


3. Async/Await


在一段代码中调用 async,或者更明确地调用方法,这个方法都会在另一个线程上执行,不会阻塞当前线程。当代码运行到 await 命令的时候,它会继续运行(await 的语句)。如果这时 async 代码还没有完成,那么执行中的程序会返回到调用点。


这有助于提高应用程序总体的响应速度,以及减少性能瓶颈。在应用程序访问 Web 和进行所有 UI 相关的活动时,使用异步程序非常重要。相对于以前的异步编程实现,使用 async/await 可以保留你代码的逻辑结构,而编译器则会担负起以前由开发者担负的重担。


示例:


class Program

    {

        public static void Main()

        {

            Console.WriteLine("Hey David, How much is 98745 divided by 7?");


            Task<int> david = ThinkAboutIt();


            Console.WriteLine("While he thinks, lets chat about the weather for a bit.");

            Console.WriteLine("Do you think it's going to rain tomorrow?");

            Console.WriteLine("No, I think it should be sunny.");


            david.Wait();

            var davidsAnswer = david.Result;


            Console.WriteLine($"David: {davidsAnswer}");


            Console.ReadKey();

        }


        private static async Task<int> ThinkAboutIt()

        {

            await ReadTheManual();


            Console.WriteLine("Think I got it.");


            return (98745 / 7);

        }


        private static async Task ReadTheManual()

        {

            string file = @"D:\HowToCalc.txt";


            Console.WriteLine("Reading a manual.");

            

            using (StreamReader reader = new StreamReader(file))

            {

                string text = await reader.ReadToEndAsync();

            }


            Console.WriteLine("Done.");

        }

}


输出:


// Possible Output:


Hey David, How much is 98745 divided by 7?

Reading a manual.

While he thinks, lets chat about the weather for a bit.

Do you think it's going to rain tomorrow?

No, I think it should be sunny.

Done.

Think I got it.

David: 14106


概要:CompletableFutures 无疑可以使我们更趋近于拥有等效于 C# 和 Java 所拥有的异步编程中的能力。尽管如此,使用它所带来的复杂性使其易用度不能与使用 async /await 关键字进行的实现相提并论。


4. Lazy<T> 类


无论使用 C# 还是 Java,很多人都已经实现了延迟初始化 (或实例化),因此对象要在第一次使用的时候才会被创建。有一种常见的例子是将延迟初始化用于应用程序启动的时候加载大量对象,但实际需要初始化的对象可能只有少数几个。这种情况下,我们希望辨别哪些是不需要在这里初始化的。只初始化那些确实需要初始化的对象可以提升应用程序的性能。


小结:最近,Lambda 表达式引入到 Java 8 之后,在 Java 中实现延迟加载(还有不少其它事情)变得更容易了。不过,在 C# 中我们可以使用语义化的 Lazy<T> 封装类来延迟初始化任何类库或用户指定的类型。


5. 一些等价的关键词


语言中的有用功能不一定像在 C# 中的 LINQ 或 Java 中的模块一样大。这里有一些可以帮助 C# 开发人员的关键字,它们在 Java 中并没有:


  • as


C# 中的 as 关键字会尝试安全地将对象转换为某个类型,如果不能转换的话,就返回 null。与 Java 的instanceof 几乎等同,但它是一个布尔值,如果类型匹配则返回 true,否则返回 false。


  • Yield


在 C# 中使用  Yield 和 return yield 来进行自定义且状态化的迭代,不需要显式创建额外的类,也不需要创建临时集合。在 Java 中我们实现迭代最好的选择是使用外部库或使用 Java 8 引入的 Lambda 表达式。


  • var


Var 是一种隐式类型,其实际类型由编译器决定,其功能相当于写一个显式类型 (比如 int, string 等)。它除了可以减少一些按键之外,var 还允许用于匿名类型,而匿名类型在 LINQ 中很常用。我们期待看到“var”标识,备受瞩目的 Java SE 9 将实现“将类型推导扩展到定义并初始化局部变量时。”


  • Checked


C# 中,我们使用 checked 关键字显式启用对整型表达式的溢出检查。如果表达式的运算结果超出目标类型的范围,我们可以使用 checked 强制要求运行时抛出 OverflowException。这十分有用,因为常量表达式会在编译期进行溢出检查,而非常量表达式不会。


工具生态系统


Java 和 C# 之间存在大量的不同之外,当然,其中一些源于 Java 和 .NET 框架的不同。这些不同之处也导致了一些工具在兼容性方面的差异,比如 OverOps 在生产监控和错误跟踪方面的差异。


先学习 C# 还是 Java?


那么究竟是选择哪一种编程语言会更有前景呢?在此,也分享一些网友的看法:


软件开发者 Jerome Terry:


语言其实都是相似的,学到的顺序并不重要。


企业中 Java 更受欢迎,也更容易找到工作。 如果学习了 Java 并熟悉 JVM,则可以轻松切换到 Scala 或 Clojure;C#适合写 Windows 程序,上手要更加容易。如果你想要在 Windows 上开发,那么首选必然为 C#。 


但是如果只能选一种语言,我可能更倾向于 Java。


COBOL 开发者 Wim ten Brink:


首先学习 C 如何?先花一周的时间学习 C 语言的语法,然后再花两周时间学习 C++。


一旦你有了基本的这种语法的经验,就可以开始看 Java 和 C#,比较两者功能特性。你就会发现它们之间会与 C/C++ 有些相似。无论如何,学习这两者并不是一个巨大的问题。


如果你问我将来最流行的语言,因此会最有用,那么最新的 TIOBE 编程语言排行榜来看,Java 仍会是 No.1,而 C# 处于 C 和 C ++ 之后的第四名。这基本上表明它们在互联网上的受欢迎程度。



但根据个人的经验来看,C# 更实用。


整体而言,在如今开发的大环境之下,更多人愿意去选择 Java 。一位已有 3 年 C# 开发经验的 CSDN 博主也表示:


已经做 C# 开发快 3 年了,马上 30 了。感觉没啥前景,毫无出头之日的迷茫。现在大点的互联网公司都是用 Java(这只是我个人的看法)。所以现在想趁合同期结束之前,转到 Java 去。SSH 框架目前正在学。主要是没 Java 开发经验,语法上没什么问题。


我看了一些 Java 方面的招聘,绝大多数都要对 SSH、SSM 框架熟悉。所以我就想,先把框架花半个月看下,我是看的网上的视频,学起来很快。然后再具体花一个月时间详细研究一下 SSH 网上商城之类的源代码。然后把这当成项目经验写进简历之类。


发觉换工作真心难。工资低了不想干,高了别人又看不起,说方向不匹配。实在觉得 Java 能做的事情非常多,工资待遇方面其实跟 C# 也基本持平。但是越往上,个人觉得还是 Java 更坚挺一些。


欢迎在下方留言,分享你的编程语言学习之路,希望可以给后来者一点启示及思考。


————— END —————


程序员如何提高加速度,两年做到待遇 20K+?


程序员自白:为什么我们能将月入五万活得像月薪五千


技术学到多厉害,才能顺利进入 BAT?



您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存