C# 从语法角度比 Java 优秀在哪里?

虽然 java 和 .net 都不做,但是多少了解一些,两者虽然很像,请问 C# 比 java 优秀在哪里?另外 unity 选了 C# 做跨平台实现…
关注者
680
被浏览
357,828

54 个回答

已有的回答都比较旧了,这里来说几个比较新的特性 C# 8~9,以及即将实装的特性(部分在今年年底的 C# 10 会到来,另一部分会在明年年底的 C# 11 到来)。

数组切片(C# 8)

取数组第 2 个元素到倒数第 2 个元素的切片(C# 的切片是借助 Span<T> 的零成本抽象),对于字符串或者其他集合也能用

array[1..^1];

返回引用(C# 8)

ref int GetElement(int[] array, int index) => ref array[index];
var array = new [] { 1, 2, 3, 4, 5 };
ref var v = ref GetElement(array, 2);
v = 0;
// 1 2 0 4 5

支持递归语义的模式匹配(C# 8 + C# 9)

如何化简双重否定 not not a 这个表达式?

Expression SimplifyDoubleNot(Expression expression)
    => expression is UnaryExpression
            NodeType: ExpressionType.Not,
            Operand: UnaryExpression
                NodeType: ExpressionType.Not,
                Operand: var innerExpression
        } ? innerExpression : expression;

接口虚/抽象静态方法(C# 10)

如何表达支持 + 运算符的接口?

interface IAddable<T> where T : IAddable<T>
    static abstract T operator +(T left, T right);

使用:

T Foo<T>(T left, T right) where T : IAddable<T> => left + right;

Type Classes(C# 11)

如何让已有但我不拥有的类型 MyFoo 支持 IAddable<T>

extension MyBar : MyFoo, IAddable<MyFoo>
    public static MyFoo operator +(MyFoo left, MyFoo right) => ...;

使用:

MyFoo x = new(), y = new();
var result = x + y;

Records + Discriminated Unions(C# 8 + C# 11)

如何定义四则代数运算表达式?

enum class Operator { Add, Sub, Mul, Div }
enum class Expr<T> where T : IAddable<T>, ISubable<T>, IMulable<T>, IDivable<T>
    ConstExpr(T val),
    BinaryExpr(Expr<T> left, Expr<T> right, Operator op)

配合模式识别对表达式进行计算:

T Eval(Expr<T> expr) => expr switch
    ConstExpr(var val) => val,
    BinaryExpr(var left, var right, var op) => op switch
        Add => Eval(left) + Eval(right),
        Sub => Eval(left) - Eval(right),
        Mul => Eval(left) * Eval(right),
        Div => Eval(left) / Eval(right)

优秀得太多了

1:空值判断

在Java里面null是不能作比较的(==null除外),例如下面代码在Java里面会抛异常,在C#里类似的代码能正常运行

Integer i=null;
if(i==1){}
if(1==i){}
switch(i){}

所以Java里面每次使用if或者swich之前,最好先判断一下里面的变量是不是空值, 而写C#代码就没有这种顾虑。

2:字符串相等判断

在Java里面不能使用==判断两个字符串是否相等,要使用equals方法,但不要以为把==改成equals方法就完事了,还要考虑空值问题,例如以下代码很明显因为调用equals的对象是null会抛异常

String s=null;
s.equals("");

在C#里面就没有这种顾虑了,==两头哪边是空值都没问题。但在Java里面int这些基础类型又可以使用==判断是否相等,因为Java不支持操作符重载,你想搞得String也统一使用==判断是否相等是不可能。但回过头来,判断一个字符串是不是null又不能用equals,又得用上==了。

Java的这个字符串判断方式连第三方组件都看不过眼,在jsp和MyBatisd的xml文件里面,都支持使用==判断两个字符串是否相等。

我做事比较粗心大意,总有忘记了要用equals,写成==的时候,导致相关代码总返回false。

3:失败的泛型设计

Java的泛型我现在还没有驾驭它的能力,我曾经试过通过泛型去定义一个把传入的Map转换成目标类型对象的方法:

public T ConvertMpToObject<T>(Map map)
    return null;
}

就这样在C#看起来很平常的方法,在Java里面无法通过语法检测,编译不起来,真是连TypeScript都不如啊。

后来我还了解到,Java的泛型还有一个“类型刷除”的特性,无法在运行时获取泛型的类型,也就是说上面方法就算能通过编译也没有用,如果不增加额外的参数,则无法获取T的类型来创建新对象。

其实Java那个泛型我估计它自己都没有完全搞明白,举个例子:

Map<Integer,Integer> map=new HashMap<>();
int value=map.get("abc");

这段代码d中的"abc"类型没有对上定义变量map时限定的Integer,但居然可以编译通过!

对应的C#代码是不可能通过编译的:

Dictionary<int,int> dictionary=new Dictionary<int,int>();
int value=dictionary["abc"];