c# 是 typescript 的最佳替補?

c# 是 typescript 的最佳替補?

typescript非常優秀。它完美地結合了強類型和快速開發,因此非常好用,我在許多情況下都會默認選擇這個庫。但是,世上沒有完美的語言,有些情況下typescript並不是最合適的工具:

最后更新 2021/12/27 下午8:53
CSDN
预计阅读 13 分钟
分类
.NET
标签
.NET C# TypeScript

作者|nate hill

譯者|彎月

出品|csdn(id:csdnnews)

typescript 非常優秀。它完美地結合了強類型和快速開發,因此非常好用,我在許多情況下都會默認選擇這個庫。但是,世上沒有完美的語言,有些情況下 typescript 並不是最合適的工具:

  1. 性能至關重要(例如實時通信、視頻遊戲)
  2. 需要與原生代碼(如 c/c++或 rust)交互
  3. 需要更嚴格的類型系統(例如金融系統)

對於這些情況,typescript 開發人員最好還是選用其他語言。c#、go 和 java 都是非常好的選擇。它們的速度遠超 typescript,每種語言都有自己的長處。c#能與 typescript 配合得很好,我來解釋一下為什麼。

图源:CSDN 付费下载自东方 IC

1. typescript 就是添加了 c# 的 javascript

c#能與 typescript 配合得很好,因為它們看上去就像是同一種語言。兩者都是由 anders hejlsberg 設計的,而且從許多方面來看,typescript 就是添加了 c#的 javascript。它們的特性和語法都很相似,因此在同一個項目中結合使用二者非常容易。更重要的是,c#的語言與 typescript 很相似,因此開發人員閱讀和編寫代碼也非常輕鬆。 相反,go 是一種完全不同的語言:沒有類,沒有繼承,沒有異常,沒有包級別的封裝(只有類級別的封裝),而且語法也完全不同。當然這並不一定是壞事,但開發人員的確需要重新思考並用不同的方式設計代碼,因此,同時使用 go 和 typescript 是比較困難的。不過,java 與 c#很相似,但依然缺乏許多 c#和 typescript 都有的功能。

2. c#和 typescript 的相似之處

也許你已經知道,c#和 typescript 有很多相似之處,如基於 c 的語法、類、接口、泛型等。下面,我來詳細列舉一下二者的相似之處:

  • 2.1 async/await
  • 2.2 lambda 表達式和函數式數組方法
  • 2.3用於處理空的操作符(?,!,??)
  • 2.4解構
  • 2.5命令行界面(cli)
  • 2.6基本功能(類、泛型、錯誤和枚舉)

2.1 async/await

首先,c#和 javascript 都使用 async/await 來處理異步代碼。在 javascript 中,異步操作用 promise 表示,而應用程式可以 await 一個異步操作結束。c#中的 promise 其實是 task,概念上與 promise 完全相同,也有相應的方法。下面的例子演示了兩種語言中 async/await 的用法:

typescript 中 async/await 的例子:

async function fetchAndWriteToFile(url: string, filePath:string): Promise<string> {
  // fetch() returns aPromise
  const response = awaitfetch(url);
  const text = awaitresponse.text();
  // By the way, we'reusing Deno (https://deno.land)
  awaitDeno.writeTextFile(filePath, text);
  return text;
}

c#中 async/await 的例子:

using System.IO;
using System.Net.Http;
using System.Threading.Tasks;

async Task<string> FetchAndWriteToFile(string url, stringfilePath) {
  // HttpClient.GetAsync()returns a Task
  var response = await newHttpClient().GetAsync(url);
  var text = awaitresponse.Content.ReadAsStringAsync();
  awaitFile.WriteAllTextAsync(filePath, text);
  return text;
}

下面是JavaScriptPromise API 与等价的C# Task API:

JavaScript API 等價的 c# api
Promise.all() Task.WaitAll()
Promise.resolve() Task.FromResult()
Promise.reject() Task.FromException()
Promise.prototype.then() Task.ContinueWith()
new Promise() new TaskCompletionSource()

2.2 lambda 表達式和函數式數組方法

C#JavaScript都用熟悉的=>语法(即箭头函数)来表示lambda表达式。下面是TypeScriptC#的比较:

typescript 中使用 lambda 表達式:

const months = ['January', 'February', 'March', 'April'];
const shortMonthNames = months.filter(month => month.length< 6);
const monthAbbreviations = months.map(month =>month.substr(0, 3));
const monthStartingWithF = months.find(month => {
  returnmonth.startsWith('F');
});

c#中使用 lambda 表達式:

using System.Collections.Generic;
using System.Linq;

var months = new List<string> {"January","February", "March", "April"};
var shortMonthNames = months.Where(month => month.Length <6);
var monthAbbreviations = months.Select(month =>month.Substring(0, 3));
var monthStartingWithF = months.Find(month => {
  returnmonth.StartsWith("F");
});

上述示例演示了C#System.Linq命名空间中的一些方法,相当于JavaScript函数式数组方法。下面是JavaScript数组方法与等价的C# Linq方法:

JavaScript API 等價的 c# api
Array.prototype.filter() Enumerable.Where()
Array.prototype.map() Enumerable.Select()
Array.prototype.reduce() Enumerable.Aggregate()
Array.prototype.every() Enumerable.All()
Array.prototype.find() List.Find()
Array.prototype.findIndex() List.FindIndex()

2.3處理空操作符

c#和 typescript 處理空的特性也一樣:

Feature name Syntax Documentation links
Optional properties property? TS :https://www.typescriptlang.org/docs/handbook/2/objects.html#optional-properties,
C#:https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/nullable-reference-types
Non-null assertion object!.property TS:https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#non-null-assertion-operator,
C#:https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-forgiving
Optional chaining object?.property JS :https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining,
C#:https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/member-access-operators#null-conditional-operators--and-
Nullish coalescing object ?? alternativeValue JS:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator,
C#:https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-coalescing-operator

2.4解構

儘管 c#默認不支持數組或類的解構,但它支持 tuple 和 record 的解構,用戶也可以為自定義類型定義解構。下面是 typescript 和 c#中解構的例子:

typescript 中解構的例子:

const author = { firstName: 'Kurt', lastName: 'Vonnegut' };
// Destructuring an object:
const { firstName, lastName } = author;

const cityAndCountry = ['Indianapolis', 'United States'];
// Destructuring an array:
const [city, country] = cityAndCountry;

c#中解構的例子:

using System;

var author = new Author("Kurt", "Vonnegut");
// Deconstructing a record:
var (firstName, lastName) = author;

var cityAndCountry = Tuple.Create("Indianapolis","United States");
// Deconstructing a tuple:
var (city, country) = cityAndCountry;

// Define the Author record used above
record Author(string FirstName, string LastName);

2.5命令行界面(cli)

我的开发方式是使用文本编辑器编写代码,然后在终端运行命令,构建并运行。对于TypeScript,这意味着需要使用node或deno命令行界面(CLI)C#也有类似的CLI,名为dotnet(由 C#的.NET 运行时得名)。下面是使用dotnet CLI的一些例子:

mkdir app && cd app
# Create a new console application
# List of available app templates:https://docs.microsoft.com/dotnet/core/tools/dotnet-new
dotnet new console
# Run the app
dotnet run
# Run tests (don't feel bad if you haven't written those)
dotnet test
# Build the app as a self-contained
# single file application for Linux.
dotnet publish -c Release -r linux-x64

2.6基本功能(類、泛型、錯誤和枚舉)

這些是 typescript 和 c#之間更基本的相似性。下面的例子是有關這幾個方面的居間:

typescript 類的示例:

import { v4 as uuidv4 } from'https://deno.land/std/uuid/mod.ts';

enum AccountType {
  Trial,
  Basic,
  Pro
}

interface Account {
  id: string;
  type: AccountType;
  name: string;
}

interface Database<T> {
  insert(item: T):Promise;
  get(id: string):Promise<T>;
}

class AccountManager {

  constructor(database:Database<Account>) {
    this._database =database;
  }

  asynccreateAccount(type: AccountType, name: string) {
    try {
      const account = {
        id: uuidv4(),
        type,
        name;
      };
      awaitthis._database.insert(account);
    } catch (error) {
        console.error(`Anunexpected error occurred while creating an account. Name: ${name}, Error:${error}`);
    }
  }

  private _database:Database<Account>;
}

c#類的示例:

using System;
using System.Threading.Tasks;

enum AccountType {
  Trial,
  Basic,
  Pro
}

record Account(string Id, AccountType Type, string Name);

interface IDatabase<T> {
  Task Insert(T item);
  Task<T> Get(stringid);
}

class AccountManager {

  publicAccountManager(IDatabase<Account> database) {
    _database = database;
  }

  public async voidCreateAccount(AccountType type, string name) {
    try {
      var account = newAccount(
       Guid.NewGuid().ToString(),
        type,
        name
      );
      await_database.Insert(account)
    } catch (Exceptionexception) {
     Console.WriteLine($"An unexpected error occurred while creating anaccount. Name: {name}, Exception: {exception}");
    }
  }

  IDatabase<Account>_database;
}

3. c#的其他優勢

與 typescript 相似並不是 c#的唯一優點,它還有其他優點:

  • 3.1與原生代碼結合更容易
  • 3.2事件
  • 3.3其他功能

3.1與原生代碼結合

C#的最大优势之一就是它可以深入原生代码。本文开头提到,TypeScript不擅长与C/C++代码结合Node.js有一个支持原生C/C++的插件,名为Node-API,但是它需要为原生函数编写额外的C++包裹器,将原生类型转换成JavaScript对象,或相反,类似于JNI的工作方式。而C#可以直接调用原生函数,只需把库放到应用程序的 bin 目录下,然后将 API 定义为 C#中的外部函数即可。然后就能像C#函数一样使用外部函数,.NET运行时会处理好 C#数据类型与原生数据类型之间的转换。例如,如果原生库导出了下面的 C 函数:

int countOccurrencesOfCharacter(char *string, char character) {    int count = 0;
    for (int i = 0;string[i] != '\0'; i++) {
        if (string[i] ==character) {
            count++;
        }
    }
    return count;
}

那麼可像下面這樣從 c#中調用:

using System;
using System.Runtime.InteropServices;

var count = MyLib.countOccurrencesOfCharacter("C# is prettyneat, eh?", 'e');
// Prints "3"
Console.WriteLine(count);

class MyLib {
    // Just placeMyLibraryName.so in the app's bin folder
   [DllImport("MyLibraryName")]
    public static externint countOccurrencesOfCharacter(string str, char character);
}

这种方法可以通过C连接访问任何动态库(.so.dll.dylib),也就是说,你可以轻松地调用CC++RustGo其他语言编写的代码,只要编译成机器码即可。原生交互的其他应用还有:

  • 將指針作為 intptr 傳給原生對象
  • 利用GetFunctionPointerForDelegate()C#方法作为函数指针传给原生函数
  • 使用Marshal.PtrToStringAnsi()C字符串转换为C#字符串
  • 轉換結構和數組

3.2事件

C#的一个独特的特性是,提供了一流的事件支持。在TypeScript中,你可以实现addEventListener()方法,让客户端监听事件,而C#event关键字,可以用来定义事件,并通过简单的语法将事件通知给所有监听者(而不需要像TypeScript那样手动遍历所有事件监听者并在try/catch块中执行)。例如,我们可以让Connection类定义一个MessageReceived事件,如下所示:

class Connection {
    // AnAction<string> is a callback that accepts a string parameter.
    public eventAction<string> MessageReceived;
}

使用Connection代码可以通过+=操作符给MessageReceived添加一个处理函数,如下:

var connection = new Connection();
connection.MessageReceived += (message) => {
   Console.WriteLine("Message was received: " + message);
};

Connection类可以在内部调用MessageReceived,为所有监听者触发MessageReceived事件:

// Raise the MessageReceived event
MessageReceived?.Invoke(message);

4. 其他優勢

  1. 性能: C#很快。C#ASP.NET(Core) Web框架一直在Techempower的评测中名列前茅,而C#.NET CoreCLR运行时的性能每个主要版本都在提高。C#拥有优良性能的原因之一是,通过使用结构而不是类,应用程序可以最小化甚至完全消除垃圾回收。因此,C#视频游戏编程中非常流行。
  2. 游戏和混合现实: C#是游戏开发最流行的语言之一,像UnityGodot甚至Unreal游戏引擎都使用了C#C#混合现实中也很流行,因为VRAR应用程序都是用Unity编写的。
  3. 由于C#拥有第一方库、工具和文档,因此一些任务非常容易实现,比如,在C#中创建gRPC客户端要比TypeScript方便得多。相反,在Node.js中使用TypeScript时,就必须找出正确的模块和工具的组合,才能正确地生成JavaScript gRPC客户端,以及相应的TypeScript类型。
  4. 高级功能:C#有许多其他语言没有的功能,如运算符重载析构函数等。

5. 總結

如前所述,世上没有完美的语言。在设计语言时总要有所权衡,所以一些语言的速度更快,但使用难度会增加(例如Rust的借出检查)。另一方面,一些语言非常易用,但通常性能的优化难度就会增加(例如JavaScript的动态语言特性)。正因如此,我相信掌握一组相似的语言会非常有用:这些语言分别有各自的长处,但都很相似,而且能互相配合。例如,下面是我选择的一组语言:

5.1 TypeScript

  • 最高層的語言,開發速度最快
  • 性能並非最佳,但適用於大多數應用
  • 不太適合與原生代碼結合

5.2 C#

  • 仍然是高级语言,支持垃圾回收,所以很容易使用,尽管并不如TypeScript那么容易。
  • 从速度和内存占用量来看,其性能都优于 TypeScript
  • 最重要的是,能夠與底層很好地結合

C++

  • 開發難度較大(例如需要手動內存管理),因此開發速度會慢很多
  • 但運行時的性能最佳!而且隨處可用,能與許多已有的軟體相結合
  • 很像C#,而且标准库很好,但也有许多陷阱(大多数与内存管理有关)。我更希望使用Rust,因为它的内存安全性更好,但我的许多工作都要与已有的C++代码结合,因此使用C++会更容易。

參考連結:https://nate.org/csharp-and-typescript

Keep Exploring

延伸阅读

更多文章
同分类 / 同标签 2026/2/7

aot使用經驗總結

從項目創建伊始,就應養成良好的習慣,即只要添加了新功能或使用了較新的語法,就及時進行 aot 發布測試。

继续阅读