著者は|ネイト·ヒル
翻訳者。|ベンド·ムーン
生産された|CSDN(ID:CSDNnews)
TypeScriptは素晴らしいです。強力な型付けと迅速な開発を完璧に兼ね備えているので、非常に使いやすく、多くの場合デフォルトでこのライブラリを選択しています。しかし、完璧な言語は存在せず、TypeScriptが最適なツールではない場合もあります。
- パフォーマンスが重要(リアルタイム通信、ビデオゲームなど)
- C/C++やRustなどのネイティブコードとの対話が必要
- より厳格な制度が必要(金融システムなど)
このような場合、TypeScript開発者は他の言語を選択する方が良いでしょう。C#、Go、Javaはすべて素晴らしい選択肢です。TypeScriptよりもはるかに高速で、それぞれの言語には独自の利点があります。C#はTypeScriptとうまく動作します。その理由を説明しましょう。

1. TypeScriptはC#に追加されたJava Scriptです。
C#はTypeScriptとうまく動作します。同じ言語のように見えるからです。どちらもAn ders Hejlsbergによって設計され、TypeScriptは多くの点でC#のJava Scriptを追加したものです。機能と構文は非常に似ているので、同じプロジェクトで両方を組み合わせるのは簡単です。さらに、C#の言語はTypeScriptに似ているため、開発者はコードを読み書きするのが簡単です。 代わりに、Goは全く異なる言語です。クラスも継承も例外もなく、パッケージレベルのカプセル化もなく(クラスレベルのカプセル化のみ)、構文も全く異なります。これは必ずしも悪いことではありませんが、開発者は再考し、コードを異なる方法で設計する必要があるため、GoとTypeScriptの両方を使用するのは難しいです。JavaはC#に似ていますが、C#やTypeScriptに見られる機能の多くはまだ欠けています。
2. C#とTypeScriptの類似点
ご存知のように、C#とTypeScriptには、Cベースの構文、クラス、インターフェイス、ジェネリックなど、多くの類似点があります。以下に、両者の類似点を詳しく説明します。
- 2.1 async/await
- 2.2ラムダ式と関数配列メソッド
- 2.3空の処理に使用する演算子!
- 2.4解体する。
- 2.5 CLI(コマンド·ライン·インタフェース)
- 2.6基本機能(クラス、ジェネリック、エラー、列挙)
2.1 async/await
まず、C#とJava Scriptは非同期コードを処理するためにasync/awaitを使用します。Java Scriptでは、非同期操作はPromiseで表され、アプリケーションは非同期操作の終了を待つことができます。C#のPromiseは実際にはタスクであり、概念的には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;
}
下面是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() |
2.2ラムダ式と関数型配列メソッド
C#和JavaScript都用熟悉的=>语法(即箭头函数)来表示lambda表达式。下面是TypeScript和C#的比较:
TypeScriptでのラムダ式の使用:
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#でラムダ式を使用する:**
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#はデフォルトで配列やクラスの分解をサポートしていないが、タプルやレコードの分解をサポートしており、カスタム型の分解を定義することもできる。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),也就是说,你可以轻松地调用C、C++、Rust、Go或其他语言编写的代码,只要编译成机器码即可。原生交互的其他应用还有:
- 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. その他の利点
- 性能:
C#很快。C#的ASP.NET(Core) 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#有许多其他语言没有的功能,如运算符重载、析构函数等。
5. まとめまとめまとめ
如前所述,世上没有完美的语言。在设计语言时总要有所权衡,所以一些语言的速度更快,但使用难度会增加(例如Rust的借出检查)。另一方面,一些语言非常易用,但通常性能的优化难度就会增加(例如JavaScript的动态语言特性)。正因如此,我相信掌握一组相似的语言会非常有用:这些语言分别有各自的长处,但都很相似,而且能互相配合。例如,下面是我选择的一组语言:
5.1 TypeScript
- 最高レベルの言語、最速の開発
- パフォーマンスは最適ではないが、ほとんどのアプリケーションに適している
- ネイティブコードとの組み合わせには不向き。
5.2 C#
- 仍然是高级语言,支持垃圾回收,所以很容易使用,尽管并不如
TypeScript那么容易。 - 从速度和内存占用量来看,其性能都优于
TypeScript - 最も重要なのは、底とうまく統合することです。
C++
- 開発が難しく(手動でメモリ管理が必要ななど)、開発がはるかに遅くなります。
- 実行時に最高のパフォーマンス!多くの既存のソフトウェアと組み合わせることができます。
- 很像
C#,而且标准库很好,但也有许多陷阱(大多数与内存管理有关)。我更希望使用Rust,因为它的内存安全性更好,但我的许多工作都要与已有的C++代码结合,因此使用C++会更容易。
参照先リンクhttps//nate.org/csharp-and-typescript