Java和C#的5个差异

785 阅读4分钟

Java 和 C# 之间有很多相似之处——它们都是受 C 影响的语法有些相似的托管语言,但也有很多不同之处。在这篇博文中,我们将探讨 Java 开发人员在过渡到 C# 时应该注意的一些关键区别。

1 - 语法糖

让我们探讨 C# 提供的三种语言功能,以增强代码的可读性,而这些功能在 Java 中是不存在的。这绝不是一个详尽的清单。

命名参数和可选参数
在 C# 中,您可以使用参数的名称以任意顺序指定方法的参数。例如,考虑以下方法:

static void PrintHello(string name, string surname) =>
            Console.WriteLine($"Hello, {name} {surname}!");

以下调用在结果方面是相同的:

PrintHello("John", "Doe");
PrintHello(name: "John", surname: "Doe");
PrintHello(surname: "Doe", name: "John");

此外,您可以为参数指定一个默认值,在这种情况下它变成可选的:

static void PrintHello(string value="World") =>
            Console.WriteLine($"Hello, {value}!");

static void Main(string[] args)
{
    PrintHello();
    PrintHello("Peggy");
}

获取/设置属性

C# 中的属性是一个成员,它提供了一种访问、修改或计算私有字段值的通用方法。它类似于 Java 中的常规 getter/setter,具有特殊语法和自动实现它们的选项。

考虑以下具有自动实现属性的示例:

class Student
{
    public string Name {get; init;}

    public string? Department {get; set;}
}

在这里我们声明了自动实现的属性。该Name属性公开了 get 属性,以及只能在方法 ( init) 初始化期间使用的 setter。该Department属性公开了 getter 和 setter。然后我们可以相应地访问和修改属性:

var student = new Student { Name = "Alice", Department = "Computer Science"};
student.Department = "Chemistry";
Console.WriteLine(student.Department);
Console.WriteLine(student.Name);

Null-Conditional (Elvis) 和 Null-coalescing 运算符

与 Java 不同,C# 支持 Elvis ( ?.) 和 Null 合并运算符 ( ??)。这些运算符旨在简化使用可空值的工作。例如,这段代码:

string department = null;
if (student != null) {
    department = student.Department;
}

可以替换为:

string department = student?.Department;

空合并运算符允许将val != null ? val : defaultValue表达式替换为val ?? defaultValue. 它还可以与 Elvis 运算符结合使用,例如:

Console.WriteLine(student?.Department ?? "Value not known");

2 - 异常

在 Java 中,有所谓的已检查异常和未检查异常。已检查异常是在编译时检查的,如果您使用抛出已检查异常的方法,则必须在代码中对其进行处理,否则将无法编译。另一方面,不必在代码中处理未经检查的异常。如果他们没有被抓住,他们会破坏他们抛出的线程。

C# 没有检查异常的概念,这始终由开发人员决定如何以及何时处理异常。

3 - IEnumerable 和 LINQ

C# 中的IEnumerable接口类似于Java 中的 Iterable接口 为了支持对 Iterable 对象的惰性计算,Java 8 引入了一个单独的 Stream API。在 C# 中,类似的功能直接通过Enumerable 类 提供。

下面是在C#中使用Enumerable的Where方法过滤偶数的例子:

IEnumerable<int> evenNums = Enumerable.Range(1, 1000)
                .Take(5)
                .Where(num => num % 2 == 0);

foreach (var num in evenNums)
{
    Console.WriteLine(num);
}

在 Java 中,相同的代码将如下所示:

List<Integer> evenNums = Stream.iterate(1, i -> i + 1)
        .limit(5)
        .filter(num -> num % 2 == 0)
        .toList();

evenNums.forEach(System.out::println);

除了如上例所示调用查询方法外,C# 还提供语言集成查询 ( LINQ  ) 功能。这是一种允许直接在 C# 中对 IEnumberable 对象使用查询语言的技术。

过滤偶数的示例在 LINQ 样式语法中如下所示:

var nums = new List<int> { 1, 2, 3, 4, 5 };

IEnumerable<int> evenNums = from num in nums
                                where num % 2 == 0
                                select num;

foreach (var num in evenNums)
{
    Console.WriteLine(num);
}

4 - 异步/等待

C# 提供了 async/await 关键字来以更具可读性和顺序性的方式编写异步代码。考虑以下示例,我们在其中声明一个异步方法,该方法在延迟 2 秒后生成一个随机整数。我们首先调用该方法两次以生成两个随机整数,然后在需要打印总和时等待结果:

static async Task<int> RandomIntAsync()
{
    await Task.Delay(TimeSpan.FromSeconds(2));
    return new Random().Next(0, 100);
}

static async Task Main(string[] args)
{
    Task<int> val1 = RandomIntAsync();
    Task<int> val2 = RandomIntAsync();
    Console.WriteLine(await val1 + await val2);
}

5 - 可为空

默认情况下,C# 将所有值类型变量视为不可空的。要允许空值,类型必须显式标记为可为空,只需在类型定义中添加一个问号即可(例如“int?”)。这允许开发人员处理数据可能丢失或未定义的情况,例如:

class Person
{
    public string Name { get; init; }

    public int? Age { get; init; }
}

static async Task Main(string[] args)
{
    var person = new Person { Name = "Bob"};
    if (person.Age == null)
    {
        Console.WriteLine("Age not specified.");
    }
}

除了可空值类型之外,您还可以指定可空引用类型,就像我们在上面的 Student 类示例中所做的那样,我们将 Department 字符串声明为可空。从历史上看,在 C# 中,默认情况下所有引用都可以为 null。但是从 C# 8 开始,默认情况下假定引用不可为空,除非明确说明,类似于值类型的行为方式。由于向后兼容的复杂性,默认情况下不启用此功能。

结论

在这里,我们简要概述了 C# 和 Java 之间的一些(但不是全部)主要差异。希望这能为从 Java 切换到 C# 提供一个起点!