译者 | Nate Hill 翻译者 | 弯月
公司出品 | CSDN(ID:CSDNnews)
TypeScript十分杰出。它轻松地紧密结合了强类别和加速合作开发,因而十分称心,我在很多情形下单厢预设优先选择那个库。但是,人世间没轻松的词汇,很多情形下TypeScript并并非合适的辅助工具:
操控性非常关键(比如动态通讯、音频格斗游戏)
须要与原生植物标识符(如C/C++或Rust)可视化
须要更严苛的类别控制系统(比如金融创新控制系统)
对那些情形,TypeScript合作开发者最合适却是换用其它词汇。C#、Go和Java都是十分好的优先选择。它的速率远超 TypeScript,五种词汇都有他们的特长。C#能与TypeScript相互配合得较好,我来嘿嘿为何。
图源:CSDN 订阅浏览自马可波罗 IC
TypeScript 是加进了 C# 的 JavaScript
C#能与TypeScript相互配合得较好,即使它看起来就像同一类词汇。二者都是由Anders Hejlsberg结构设计的,所以从很多各方面上看,TypeScript是加进了C#的JavaScript。它的优点和句法都很相近,因而在同一工程项目中紧密结合采用二者十分容易。更关键的是,C#的词汇与TypeScript很相近,因而合作开发者阅读和编写标识符也十分轻松。
相反,Go是一类完全不同的词汇:没类,没继承,没异常,没包级别的封装(只有类级别的封装),所以句法也完全不同。当然这并不一定是坏事,但合作开发者的确须要重新思考并用不同的方式结构设计标识符,因而,同时采用Go和TypeScript是比较困难的。不过,Java与C#很相近,但依然缺乏很多C#和TypeScript都有的功能。
C#和TypeScript的相近之处
也许你已经知道,C#和TypeScript有很多相近之处,如基于C的句法、类、接口、泛型等。下面,我来详细列举一下二者的相近之处:
async/await
lambda表达式和函数式数组方法
用于处理空的操作符(?,!,??)
解构
命令行界面(CLI)
async/await首先,C#和JavaScript都采用async/await来处理异步标识符。在JavaScript中,异步操作用Promise表示,而应用程序可以await一个异步操作结束。C#中的Promise其实是Task,概念上与Promise完全相同,也有相应的方法。下面的例子演示了两种词汇中async/await的用法:
async function fetchAndWriteToFile(url: string, filePath:string): Promise {// fetch() returns aPromiseconst response = awaitfetch(url);const text = awaitresponse.text();// By the way, wereusing Deno (https://deno.land)awaitDeno.writeTextFile(filePath, text);return text;
TypeScript中async/await的例子
using System.IO;using System.Net.Http;using System.Threading.Tasks;async Task FetchAndWriteToFile(string url, stringfilePath) {// HttpClient.GetAsync()returns a Taskvar response = await newHttpClient().GetAsync(url);var text = awaitresponse.Content.ReadAsStringAsync();awaitFile.WriteAllTextAsync(filePath, text);return text;
C#中async/await的例子
下面是JavaScript的Promise 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()
Lambda表达式和函数式数组方法
C#和JavaScript都用熟悉的=>句法(即箭头函数)来表示lambda表达式。下面是TypeScript和C#的比较:
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);
TypeScript中采用lambda表达式
using System.Collections.Generic;using System.Linq;var months = new List {“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#中采用lambda表达式
上述示例演示了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()
处理空操作符
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 ?? alternativeValueJShttps://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
解构
尽管C#预设不支持数组或类的解构,但它支持Tuple和Record的解构,用户也可以为自定义类别定义解构。下面是TypeScript和C#中解构的例子:
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;
TypeScript中解构的例子
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 aboverecord Author(string FirstName, string LastName);
C#中解构的例子
命令行界面(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-newdotnet new console# Run the appdotnet run# Run tests (dont feel bad if you havent written those)dotnet test# Build the app as a self-contained# single file application for Linux.dotnet publish -c Release -r linux-x64
基本功能(类、泛型、错误和枚举)那些是TypeScript和C#之间更基本的相近性。下面的例子是有关这几个各方面的介绍:
import { v4 as uuidv4 } fromhttps://deno.land/std/uuid/mod.ts;enum AccountType {Trial,Basic,Prointerface Account {id: string;type: AccountType;name: string;interface Database {insert(item: T):Promise;get(id: string):Promise;class AccountManager {constructor(database:Database) {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;
TypeScript类的示例
using System;using System.Threading.Tasks;enum AccountType {Trial,Basic,Prorecord Account(string Id, AccountType Type, string Name);interface IDatabase {Task Insert(T item);Task Get(stringid);class AccountManager {publicAccountManager(IDatabase database) {_database = database;public async voidCreateAccount(AccountType type, string name) {try {var account = newAccount(Guid.NewGuid().ToString(),type,nameawait_database.Insert(account)} catch (Exceptionexception) {Console.WriteLine($”An unexpected error occurred while creating anaccount. Name: {name}, Exception: {exception}”);IDatabase_database;
C#类的示例
C#的其它优势
与TypeScript相近并并非C#的唯一优点,它还有其它优点:
与原生植物标识符紧密结合更容易
事件
其它功能
与原生植物标识符紧密结合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 apps bin folder[DllImport(“MyLibraryName”)]public static externint countOccurrencesOfCharacter(string str, char character);
这种方法可以通过C连接访问任何动态库(.so、.dll或.dylib),也是说,你可以轻松地调用C、C++、Rust、Go或其它词汇编写的标识符,只要编译成机器码即可。原生植物可视化的其它应用还有:
将指针作为IntPtr传给原生植物对象
利用GetFunctionPointerForDelegate()将C#方法作为函数指针传给原生植物函数
采用Marshal.PtrToStringAnsi()将C字符串转换为C#字符串
转换结构和数组
事件C#的一个独特的优点是,提供了一流的事件支持。在TypeScript中,你可以实现addEventListener()方法,让客户端监听事件,而C#有event关键字,可以用来定义事件,并通过简单的句法将事件通知给所有监听者(而不须要像TypeScript那样手动遍历所有事件监听者并在try/catch块中执行)。比如,我们可以让Connection类定义一个MessageReceived事件,如下所示:
class Connection {// AnAction is a callback that accepts a string parameter.public eventAction MessageReceived;
采用Connection标识符可以通过+=操作符给MessageReceived加进一个处理函数,如下:
var connection = new Connection();connection.MessageReceived += (message) => {Console.WriteLine(“Message was received: ” + message);
而Connection类可以在内部调用MessageReceived,为所有监听者触发MessageReceived事件:
// Raise the MessageReceived eventMessageReceived?.Invoke(message);
其它优势
操控性:C#很快。C#的ASP.NET Web框架一直在Techempower的评测中名列前茅,而C#的.NET CoreCLR运行时的操控性每个主要版本都在提高。C#拥有优良操控性的原因之一是,通过采用结构而并非类,应用程序可以最小化甚至完全消除垃圾回收。因而,C#在音频格斗游戏编程中十分流行。
格斗游戏和混合现实:C#是格斗游戏合作开发最流行的词汇之一,像Unity、Godot甚至Unreal格斗游戏引擎都采用了C#。C#在混合现实中也很流行,即使VR和AR应用程序都是用Unity编写的。
由于C#拥有第一方库、辅助工具和文档,因而一些任务十分容易实现,比如,在C#中创建gRPC客户端要比TypeScript方便得多。相反,在Node.js中采用TypeScript时,就必须找出正确的模块和辅助工具的组合,才能正确地生成JavaScript gRPC客户端,以及相应的TypeScript类别。
高级功能:C#有很多其它词汇没的功能,如运算符重载、析构函数等。
总结
如前所述,人世间没轻松的词汇。在结构设计词汇时总要有所权衡,所以一些词汇的速率更快,但采用难度会增加(比如Rust的借出检查)。另一各方面,一些词汇十分易用,但通常操控性的优化难度就会增加(比如JavaScript的动态词汇优点)。正因如此,我相信掌握一组相近的词汇会十分有用:那些词汇分别有各自的特长,但都很相近,所以能互相相互配合。比如,下面是我优先选择的一组词汇:
TypeScript
最高层的词汇,合作开发速率最快
操控性并非最差,但适用于大多数应用
不太适合与原生植物标识符紧密结合
C#
仍然是高级词汇,支持垃圾回收,所以很容易采用,尽管并不如TypeScript那么容易。
从速率和内存占用量上看,其操控性都优于 TypeScript
最关键的是,能够与底层较好地紧密结合
C++
合作开发难度较大(比如须要手动内存管理),因而合作开发速率会慢很多
但运行时的操控性最差!所以随处可用,能与很多已有的软件相紧密结合
很像C#,所以标准库较好,但也有很多陷阱(大多数与内存管理有关)。我更希望采用Rust,即使它的内存安全性更好,但我的很多工作都要与已有的C++标识符紧密结合,因而采用C++会更容易。
参考链接: https://nate.org/csharp-and-typescript