October 1, 2022
By: Kevin

以Java8的视角看C#

语法层面

在许多方面,C# 语法读起来像是 Java 语法的超集,但是有一些重命名和新增的关键字。

  • 命名规范
    • 用Pascal规则来命名属性、方法、事件, 类名和命名空间

      public class HelloWorld {
          public void SayHello(string name) {}
      }
      
    • 用Camel规则来命名成员变量、局部变量和方法的参数

      public class Product {
          private string productId;
          private string productName;
              public void AddProduct(string productId,string productName){}
          }
      

函数返回值

C# tuple

可以通过tuple返回多个值

public static (string, string)
   SplitAt(this string s, int at)
   => (s.Substring(0, at), s.Substring(at));

var (baseCcy, quoteCcy) = "EURUSD".SplitAt(3);
Console.WriteLine(baseCcy);
Console.WriteLine(quoteCcy);

可以起个有意义的名字

public static (string, string)
   SplitAt(this string s, int at)
   => (s.Substring(0, at), s.Substring(at));

public static (string Base, string Quote)
   AsPair(this string ccyPair) => ccyPair.SplitAt(3);

var pair = "EURUSD".AsPair();
Console.WriteLine(pair.Base);
Console.WriteLine(pair.Quote);

工程组织和工具

Java

  1. 组织工具

    Maven已经成为了事实上的标准. pom.xml是整个项目的定义

    • 依赖的包
    • 编译管理
    • 插件
      • 自动测试
    • 项目模板
  2. 工程组织

    • 每个项目都是单独的pom.xml文件
    • 本地的多个项目使用上层依赖工具管理, 比如make

c#

  1. 组织工具

    dotent是事实标准

    • 和nuget一起管理包依赖
    • 编译管理
    • 插件, 比如 dotnet script
    • 项目模板
    • 单元测试
  2. 工程组织

    • solution 项目的Visual Studio 解决方案文件格式(.sln), 是项目的容器 结构上分为三层 1. 版本信息 2. 项目信息 3. 全局信息 参照此文章

      Microsoft Visual Studio Solution File, Format Version 12.00
      # Visual Studio Version 16
      VisualStudioVersion = 25.0.1704.0
      MinimumVisualStudioVersion = 10.0.40219.1
      Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyNunit", "MyNunit.csproj", "{FE7951F6-8BAC-4198-A3F1-06648FE444C3}"
      EndProject
      Global
              GlobalSection(SolutionConfigurationPlatforms) = preSolution
                      Debug|Any CPU = Debug|Any CPU
                      Release|Any CPU = Release|Any CPU
              EndGlobalSection
              GlobalSection(ProjectConfigurationPlatforms) = postSolution
                      {FE7951F6-8BAC-4198-A3F1-06648FE444C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
                      {FE7951F6-8BAC-4198-A3F1-06648FE444C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
                      {FE7951F6-8BAC-4198-A3F1-06648FE444C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
                      {FE7951F6-8BAC-4198-A3F1-06648FE444C3}.Release|Any CPU.Build.0 = Release|Any CPU
              EndGlobalSection
              GlobalSection(SolutionProperties) = preSolution
                      HideSolutionNode = FALSE
              EndGlobalSection
              GlobalSection(ExtensibilityGlobals) = postSolution
                      SolutionGuid = {C7B7CCE4-CEB3-4DDF-9279-0DDC617EAD51}
              EndGlobalSection
      EndGlobal
      
    • project

代码组织

命名控件(namespace) vs 包(package)

引入类型

  • java: import

    • import class/classes 引入一个包中的全部类型(type), 或者某个类型

      import java.util.*;
      import java.util.Date;
      
    • static import 引入类的所有静态成员

      import static java.lang.Math.PI;
      import static java.lang.Math.*;
      
      System.out.println("hahah "+ PI);
      
  • C# using using的含义比import丰富

    • 引入一个命名空间(namespace)

      using System;
      Console.WriteLine("hello");
      
    • C# using static 作用于某一个Type(意思是此Type的static(静态类型)可以被直接使用.

      using static System.Console;
      WriteLine("hello");
      
    • 别名 using x =

      using Unit = System.ValueTuple;
      
      public  Func<Unit> ToFunc
          ( Action action)
          => () => { action(); return default; };
      
      public  Func<T, Unit> ToFunc<T>
          ( Action<T> action)
          => (t) => { action(t); return default; };
      

Delegate vs ??

C#

| C# type               | function type      | Example                   |
|-----------------------|--------------------|---------------------------|
| Func <int, String >   | int- >string       | (int i) = > i.ToString()  |
| Func <string >        | ()- > string       | () = > "hello"            |
| Action <int >         | int- >()           | (int i)= >WriteLine(i)    |
| Func <int, int, int > | (int, int) = > int | (int a, int b) = > a + b  |
| Action                | ()- >()            | () = > WriteLine("hello") |
  1. 举例

    Delegate是一个C#类型, 代表了一个对象方法(method)的引用(reference), 在Java中我(暂时)没有找到对应物. 它更像个C的函数指针. 它有一个参数列表(0个或者多个参数), 有一个返回类型(可以是void).

    Delegate可以指向任意一个符合其函数签名的method.

    public class Calc{
        public delegate int Calculate(int input);
    
        public int Execute(Calculate calcuate, int input){
            return calcuate(input);
        }
    }
    
    public class Program{
        public static int Square(int input) => input * input;
    }
    
    
    var calculator = new Calc();
    Calc.Calculate calc = Program.Square;
    var result = calculator.Execute(calc, 5);
    Console.WriteLine($"result:{result}");
    
  2. Action和Func

    Delegate从返回值的类型来说, 可分为两类1. 有返回值的 2. 无返回值的 有了Action和Func, 我想不到我们有任何自己声明Delegate的必要性.

    1. Func 有16中可能的参数组合 Func <T, TResult >, Func <T1, T2, TResult >, Func <T1, T2… T16, TResult >

      public class Calc{
          public int Execute(Func<int, int> calcuate, int input){
              return calcuate(input);
          }
      }
      
      public class Program{
          public static int Square(int input) => input * input;
      }
      
      var calculator = new Calc();
      var result = calculator.Execute(Program.Square, 5);
      Console.WriteLine($"result:{result}");
      
    2. Action , 同样的, 也可以有16个参数, 但是不返回值 Action , Action <T1, T2 >, Action <T1, T2… T16 >

      public class Calc{
          public int Execute(Func<int, int> calcuate, Action<int> print, int input){
      
              var result =  calcuate(input);
              print(result);
              return result;
          }
      }
      
      public class Program{
          public static int Square(int input) => input * input;
      }
      
      var calculator = new Calc();
      var result = calculator.Execute(Program.Square, System.Console.WriteLine, 5);
      
    3. Predicate 也是一种Delegate类型, 等价于 Func <T, bool >. 但是从语意上更明确.

  3. Lambda

    匿名函数, 所有的Delegate可以用lambda来表示, 而且可以是async的

    public class Calc{
        public int Execute(Func<int, int> calcuate, int input){
            return calcuate(input);
        }
    }
    
    var calculator = new Calc();
    var result = calculator.Execute(x => x * x; , 5);
    Console.WriteLine($"result:{result}");
    

    lambda的形式

    • (input-parameters) = > expression 直接返回表达式的值

    • (input-parameters) = > { } 如果有返回值需要return

    • 最简单的lambda形式

      var f = () => "kevin";
      Console.WriteLine(f());
      
  4. event

    用来出发一个delegate的执行, delegate在这种情况下被成为一个callback, 这个callback有两个参数 第一个是参数是事件的发出者, 第二个是event的类型.

    public class Calc{
        public int Execute(Func<int, int> calcuate, int input){
            return calcuate(input);
        }
    }
    
    var calculator = new Calc();
    var result = calculator.Execute((x) => x * x , 5);
    Console.WriteLine($"result:{result}");
    

Java 8

| Interface name      | Arguments | Returns | Example                            |
|---------------------|-----------|---------|------------------------------------|
| Predicate <T >      | T         | boolean | Has this album been released yet?  |
| Consumer <T >       | T         | void    | Printing out a value               |
| Function <T,R >     | T         | R       | Get the name from an Artist object |
| Supplier <T >       | None      | T       | A factory method                   |
| UnaryOperator <T >  | T         | T       | Logical not (!)                    |
| BinaryOperator <T > | (T, T)    | T       | Multiplying two numbers ( *)       |

Annotation vs Attribute

都是元数据, 可以作用于class或者class的property以及method 常见的Attribute有 [Serializable] 实现方式就是个类, 类名称 XXXAttribute , 属性为 [XXX]

[Test("Attribute Test", Quantity = 10)]
internal class AttributeExample{}

public class TestAttribute : Attribute {
    private readonly string name;
    public int Quantity;

    public TestAttribute(string name){
        this.name = name;
    }

   public string Name () => name;
}
// 可以在运行时拿到
var attributes = typeof(AttributeExample).GetCustomAttributes(true);
foreach(var att in attributes){
    var attValue = (TestAttribute)att;
    Console.WriteLine($"name {attValue.Name}, Quantity: {attValue.Quantity}");
}

类型层级

  • Java: 所有类都是java.lang.Object的子类
  • C#: 所有类都是System.Object的子类

资源管理: IDisposable vs Closeable

保证资源一定会释放掉, 各类语言都逐渐增加了try-with-resources的模式, 本质就是(自动)释放资源

C#

using (var connection = new SqlConnection(connectionString)) { ... }

会被编译为:

try {
    var connection = new SqlConnection(connectionString);
    ...
} finally {
    connection.Dispose();
}

Java

Java1.7增加了AutoClosable接口

try(Resource resource = new Resource()) {
       resource.read();
   } catch (Exception e) {
   }

Clojure

(with-open [r (clojure.java.io/input-stream "myfile.txt")]
         (loop [c (.read r)]
           (if (not= c -1)
             (do
               (print (char c))
               (recur (.read r))))))

Struct

C#中的struct对应c语言中的struct,直接对应内存结构, 没有元数据, 但是也区别于c的结构体(struct), 这个C#的struct只能分配在堆上. 对应于C#中函数传值的(传入/传出)的场景,如果是struct, 只会返回value, 而class的实例是引用(reference)

  • Java没有Struct

stream vs linq

java

import java.util.stream.*;
import java.util.*;
Set<Integer> numbers = new HashSet<>(Arrays.asList(4, 3, 2, 1));
System.out.println(numbers);

代码组织

  • 库函数.jar vs .dll
  • 代码文件和package vs namespace
  • sln/proj vs pom.xml
Tags: c# Java