プログラミング C# - 翔ソフトウェア (Sho's)

C#/.NET/ソフトウェア開発など

BuriKaigi 2020 が開催されました

毎年冬に富山で開催されている BuriKaigi (*)。
今回も、90名もの方にご参加いただき、大いに盛り上がりました。

昨年までは2トラックでしたが、今回は3トラックになりました (ルームA, B, C)。
リンクなどをまとめておきます。

(*) BuriKaigi とは何かということは次に詳しく書かれています: Burikaigi という毎年冬のイベント | hikaruworld | Medium

開催概要

BuriKaigi 2020
日時 2020年2月1日(土) 13:00〜19:00 (終了後懇親会)
会場 富山県民会館 (懇親会会場: 割烹 扇)

イベントの様子

資料

ルームA
ルームB
ルームC
LT

写真・ビデオ

ブログ

新たな Web アプリケーションフレームワーク「Blazor」 ‐ ネイティブ アプリケーション開発も可能に

日本時間で昨日、「.NET Conf: Focus on Blazor」というオンライン カンファレンスが開催され、Blazor に関する新たな発表がなされた。
現在、セッション動画を観ることができる。

Blazor は、C#/.NET で Web アプリケーションを開発できるプラットフォームとして、.NET Core 3 で正式リリースされた。
従来の ASP.NET ではサーバーサイドを C#/.NET、クライアントサイドを JavaScript (や TypeScript) で記述していたが、Blazor ではサーバーサイドとクライアントサイドを統一した言語 (C#) で記述できるのが大きなメリットだ。これにより、単一のコードでモデルが記述できるようになる。

今回、Blazor に関して、以下のように多くの新技術が発表になった:

  • Blazor Server (以前発表済み)
    • .NET Core 3 で正式版
    •  Web アプリケーション (SPA: Single Page Application) 用
    • サーバーサイドでC#/.NETのコードが動作
    • クライアントサイドも JavaScript でなく C# で記述
  •  Blazor WebAssembly
    • 2020年5月に正式版の予定
    • Web アプリケーション (SPA: Single Page Application) 用
    • クライアントサイドでC#/.NETのコードが動作
    • クライアントサイドも JavaScript でなく C# で記述
  • Blazor PWA
    • 正式版は未定
    • PWA (Progressive Web Apps: デスクトップ アプリケーションのように動作するWeb アプリケーション) 用
  • Blazor Hybrid
    • 正式版は未定
    • Electron や WebView を使った HTML ベースのデスクトップ アプリケーション用
  • Blazor Native
    • 正式版は未定
    • HTML を使わないデスクトップ アプリケーション用
  • Mobile Blazor Bindings
    • 正式版は未定
    • Blazor のコードから iOSAndroid の機能を呼び出せるようにする仕組み
    • これにより Blazor で iOSAndroid のネイティブ アプリケーションを開発できる

参考資料

Boids (an artificial life program) —shoal simulator written in TypeScript—

Shos.Boids

Boids とは

Boids は、Craig Reynolds 氏によって作られた人工生命のシミュレーションだ。 Boid の名は、「Bird (鳥) oid (もどき)」から来ており、元は鳥の群れの動きを真似ている。それぞれの個体を次に上げる 3 つの規則にしたがって動かすというだけで、群れのような動きを実現している:

Boids の 3 つの規則
分離 (Separation): 近くの仲間にぶつからない方向に移動
分離 (Separation)
分離 (Separation)
整列 (Alignment): 近くの仲間が平均的に向かっている方に向かう
整列 (Alignment)
整列 (Alignment)
集結 (Cohesion): 近くの仲間の中心方向に移動する
集結 (Cohesion)
集結 (Cohesion)

参考ページ

デモ

TypeScript などで Boids のデモを 3 種類作成してみた。

鳥の群れではなく、魚の群れを意識した。

※ 各画像をクリックするとそれぞれのページへ飛ぶ

2D版: Boids (an artificial life program) —shoal simulator written in TypeScript
Boids (an artificial life program) —shoal simulator written in TypeScript
3D版: Boids (an artificial life program) 3D —shoal simulator written in TypeScript with Three.js—
Boids (an artificial life program) 3D —shoal simulator written in TypeScript with Three.js—
3D 裸眼立体視版: Boids (an artificial life program) 3D (Cross-eyed/Parallel Free-viewing Stereoscopy) —shoal simulator written in TypeScript with Three.js—
Boids (an artificial life program) 3D (Cross-eyed/Parallel Free-viewing Stereoscopy) —shoal simulator written in TypeScript with Three.js—

2D版では2次元ベクトル、3D版では3次元ベクトルで、個体の位置や速度を計算している。

2つの3D版は、Three.js というライブラリーを使って WebGL で動かしている。

参考:

3D 裸眼立体視版は、裸眼立体視 (交差法または平行法) ができる人向けだ。 立体視の方法については、次のページなどが参考になる。

参考:

なお、各版ともパラメーターを色々と変更できるので試してほしい。

これらのデモ ページの実装については、ソースコードを参照してほしい。

ソースコード

ソースコードは、GitHub に上げた:

開発環境/言語/ライブラリー

以下を使って作った:

Visual Studio Code

上記デモは、Visual Studio Code 上で作成した。

Visual Studio Code は、プログラミングに最適な Microsoft 製のテキスト エディターで、Windows 版、macOS 版、Linux 版がある。

HTML や CSS はもちろんのこと、JavaScript や TypeScript、C# などの多種多様なプログラミング言語にも対応している。

Git クライアント、ビルド、シンタックス ハイライト、リファクタリング、インテリセンスなどの機能を持ち、とても使いやすいのでお薦めだ。

どのような機能や言語が使えるかは、Extensions for Visual Studio family of products | Visual Studio Marketplace が参考になる。

参考:

TypeScript

TypeScript は、設定を変えることにより、"ES3"、"ES5"、"ES2015"、"ES2016"、"ES2017"、"ES2018"、"ES2019" にコンパイルすることができる。

デモでは、"ES5" を使っている。

TypeScript のコンパイル オプションは、"tsconfig.json"というファイルで行う。 今回は次のような内容とした。

tsconfig.json


{
  "compilerOptions": {
    /* Basic Options */
    "target": "ES5",        /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
    "module": "commonjs",   /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
    "sourceMap": true,      /* Generates corresponding '.map' file. */

    /* Strict Type-Checking Options */
    "strict": true,         /* Enable all strict type-checking options. */

    /* Module Resolution Options */
    "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
  }
}

TypeScript は、Visual Studio Code から簡単に JavaScriptコンパイルできる。

Visual Studio Code でのコンパイル
Visual Studio Code でのコンパイル

ちなみに、次のような TypeScript の場合:

class Vector3D {
    x: number;
    y: number;
    z: number;

    get absoluteValue(): number {
        return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
    }

    constructor(x: number = 0, y: number = 0, z: number = 0) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    clone(): Vector3D {
        return new Vector3D(this.x, this.y, this.z);
    }

    plus(another: Vector3D): Vector3D {
        return new Vector3D(this.x + another.x, this.y + another.y, this.z + another.z);
    }

    plusEqual(another: Vector3D): void {
        this.x += another.x;
        this.y += another.y;
        this.z += another.z;
    }

    minus(another: Vector3D): Vector3D {
        return new Vector3D(this.x - another.x, this.y - another.y, this.z - another.z);
    }

    minusEqual(another: Vector3D): void {
        this.x -= another.x;
        this.y -= another.y;
        this.z -= another.z;
    }

    multiply(value: number): Vector3D {
        return new Vector3D(this.x * value, this.y * value, this.z * value);
    }

    innerProduct(another: Vector3D): Vector3D {
        return new Vector3D(this.x * another.x, this.y * another.y, this.z * another.z);
    }

    divideBy(value: number): Vector3D {
        return new Vector3D(this.x / value, this.y / value, this.z / value);
    }

    divideByEqual(value: number): void {
        this.x /= value;
        this.y /= value;
        this.z /= value;
    }

    getDistance(another: Vector3D): number {
        return this.minus(another).absoluteValue;
    }
}

"ES5" でコンパイルしてできた JavaScript はこんな感じ:

"use strict";
var Vector3D = /** @class */ (function () {
    function Vector3D(x, y, z) {
        if (x === void 0) { x = 0; }
        if (y === void 0) { y = 0; }
        if (z === void 0) { z = 0; }
        this.x = x;
        this.y = y;
        this.z = z;
    }
    Object.defineProperty(Vector3D.prototype, "absoluteValue", {
        get: function () {
            return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
        },
        enumerable: true,
        configurable: true
    });
    Vector3D.prototype.clone = function () {
        return new Vector3D(this.x, this.y, this.z);
    };
    Vector3D.prototype.plus = function (another) {
        return new Vector3D(this.x + another.x, this.y + another.y, this.z + another.z);
    };
    Vector3D.prototype.plusEqual = function (another) {
        this.x += another.x;
        this.y += another.y;
        this.z += another.z;
    };
    Vector3D.prototype.minus = function (another) {
        return new Vector3D(this.x - another.x, this.y - another.y, this.z - another.z);
    };
    Vector3D.prototype.minusEqual = function (another) {
        this.x -= another.x;
        this.y -= another.y;
        this.z -= another.z;
    };
    Vector3D.prototype.multiply = function (value) {
        return new Vector3D(this.x * value, this.y * value, this.z * value);
    };
    Vector3D.prototype.innerProduct = function (another) {
        return new Vector3D(this.x * another.x, this.y * another.y, this.z * another.z);
    };
    Vector3D.prototype.divideBy = function (value) {
        return new Vector3D(this.x / value, this.y / value, this.z / value);
    };
    Vector3D.prototype.divideByEqual = function (value) {
        this.x /= value;
        this.y /= value;
        this.z /= value;
    };
    Vector3D.prototype.getDistance = function (another) {
        return this.minus(another).absoluteValue;
    };
    return Vector3D;
}());

class がないので、prototype と function での実装だ。

一方、"ES2019" でコンパイルしてできた JavaScript はこんな感じ:

"use strict";
class Vector3D {
    constructor(x = 0, y = 0, z = 0) {
        this.x = x;
        this.y = y;
        this.z = z;
    }
    get absoluteValue() {
        return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
    }
    clone() {
        return new Vector3D(this.x, this.y, this.z);
    }
    plus(another) {
        return new Vector3D(this.x + another.x, this.y + another.y, this.z + another.z);
    }
    plusEqual(another) {
        this.x += another.x;
        this.y += another.y;
        this.z += another.z;
    }
    minus(another) {
        return new Vector3D(this.x - another.x, this.y - another.y, this.z - another.z);
    }
    minusEqual(another) {
        this.x -= another.x;
        this.y -= another.y;
        this.z -= another.z;
    }
    multiply(value) {
        return new Vector3D(this.x * value, this.y * value, this.z * value);
    }
    innerProduct(another) {
        return new Vector3D(this.x * another.x, this.y * another.y, this.z * another.z);
    }
    divideBy(value) {
        return new Vector3D(this.x / value, this.y / value, this.z / value);
    }
    divideByEqual(value) {
        this.x /= value;
        this.y /= value;
        this.z /= value;
    }
    getDistance(another) {
        return this.minus(another).absoluteValue;
    }
}

こちらは class があるので TypeScript のコードとあまり変わらない。"ES5" とは、まるで別の言語のように見えるのが面白い。

Shos.CsvHelper (simple library for reading and writing CSV)

CSV Icon

I wrote a simple library for reading and writing CSV (the Values Comma-Separated or Character-Separated Values).

The csv format file is sometimes necessary because it can be displayed / edited with Excel and is simple. There are other libraries that read and write csv already, but I tried to make it simpler.

With this library, you can read and write plain object collections (IEnumerable<Something>) in csv format.

How to use

Overview of how to use

First, prepare something IEnumerable<TElement>:

    An IEnumerable<TElement> something
    IEnumerable<ToDo> toDoes = new ToDoList();

For example this class:

    class ToDoList : IEnumerable<ToDo> // sample collection
    {
        public IEnumerator<ToDo> GetEnumerator()
        {
            yield return new ToDo { Id = 1, Title = "filing tax returns", Deadline = new DateTime(2018, 12, 1) };
            yield return new ToDo { Id = 2, Title = "report of a business trip", Detail = "\"ASAP\"", DaySpan = new DaySpan(3), Priority = Priority.High };
            yield return new ToDo { Id = 3, Title = "expense slips", Detail = "book expenses: \"C# 6.0 and the .NET 4.6 Framework\",\"The C# Programming\"", Priority = Priority.Low, Done = true };
            yield return new ToDo { Id = 4, Title = " wish list ", Detail = " \t (1) \"milk\"\n \t (2) shampoo\n \t (3) tissue ", Priority = Priority.High };
        }

        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    }

You can write this as:

    const string csvFileName = "todo.csv";
    await toDoes.WriteCsvAsync(csvFileName);

The resulting csv file looks like this:

  • If the value contains commas, delimiters, newlines, or double quotes, these are enclosed in double quotes.
  • Double quotations in the value are replaced by two double quotations.
Id,Title,Deadline,Done,Priority,Details,DaySpan
1,filing tax returns,2018/12/01 0:00:00,False,Middle,,0
2,report of a business trip,2017/07/12 13:13:01,False,High,"""ASAP""",3
3,expense slips,2017/07/12 13:13:01,True,Low,"book expenses: ""C# 6.0 and the .NET 4.6 Framework"",""The C# Programming""",0
4, wish list ,2017/07/12 13:13:01,False,High," 	 (1) ""milk""
 	 (2) shampoo
 	 (3) tissue ",0

 

Open the created csv file in Excel
Open the created csv file in Excel

You also can read a csv file like this:

    IEnumerable<ToDo> newToDoes = await CsvSerializer.ReadCsvAsync<ToDo>(csvFileName);

Things to read and write

Public properties with both "get" and "set" of each element in the collection are read and written to csv files.

When writing:

When writing, it is converted to a string with the "ToString()" method regardless of the type.

When reading:

When reading, the string type is left as is, and enum (enumeration type) is read as the value of the string. For other types, it tries to change the string to a value using "TryParse" or "Parse". Types that cannot do either of these will not be read.

Properties that have both "get" and "set" and are of one of the following types are read:

  • String type
  • enum
  • A type that has a default constructor and can be "TryParse" or "Parse" (Basic numeric types such as int, DateTime, user-defined types with "TryParse" or "Parse")
Other rules
  • Properties with the [CsvIgnore ()] attribute are not read or written.
  • Properties with the [ColumnName("Column name")] attribute will be changed to the one with the specified column name in the csv file.

For example, the above ToDo class is like this:

    // Sample element type
    // Properties marked with ✔ will be read / written
    // Properties marked with ✖ won't be read / written
    class ToDo
    {
        public int      Id       { get; set; }                     // ✔
        public string   Title    { get; set; } = "";               // ✔
        public DateTime Deadline { get; set; } = DateTime.Now;     // ✔
        public bool     Done     { get; set; }                     // ✔
        public Priority Priority { get; set; } = Priority.Middle;  // ✔ user-defined enum 
        [ColumnName("Details")]
        public string   Detail   { get; set; } = "";               // ✔ [ColumnName ("Details")] changes the column name in the csv file to "Details"
        public DaySpan  DaySpan  { get; set; }                     // ✔ User-defined type with "Parse" (without "TryParse")
        [CsvIgnore()]
        public string   Option   { get; set; } = "";               // ✖ Ignored because [CsvIgnore()] is attached
        public string   Version => "1.0";                          // ✖ Ignored because it is only a get property
    }

The user-defined types used in the above ToDo class are as follows:

    // User-defined enum example
    enum Priority { High, Middle, Low }

    // Example of a user-defined type with "Parse" (without "TryParse")
    struct DaySpan
    {
        public int Value { get; private set; }

        public DaySpan(int value) => Value = value;
        public static DaySpan Parse(string text) => new DaySpan(int.Parse(text));
        public override string ToString() => Value.ToString();
    }

With or without header

csv can be read and written even if it has no header part (eg the first line of the csv file above).

However, if there is a header part, it can be read by collating the header part even if the column is switched, but if there is no header part, it cannot be read if the column is switched.

Example of writing without a header:

    await toDoes.WriteCsvAsync(csvFilePathName: csvFileName, hasHeader: false);

Csv file created:

1,filing tax returns,2018/12/01 0:00:00,False,Middle,,0
2,report of a business trip,2017/07/06 18:08:13,False,High,"""ASAP""",3
3,expense slips,2017/07/06 18:08:13,True,Low,"book expenses: ""C# 6.0 and the .NET 4.6 Framework"",""The C# Programming""",0
4, wish list ,2017/07/12 13:13:01,False,High," 	 (1) ""milk""
 	 (2) shampoo
 	 (3) tissue ",0

 

Open the created csv file in Excel
Open the created csv file in Excel

To read a csv file without a header:

    IEnumerable<ToDo> newToDoes = await CsvSerializer.ReadCsvAsync<ToDo>(csvFilePathName: csvFileName, hasHeader: false);    

Other specification methods

The character code can be changed (default is UTF8).

    CsvSerializer.Encoding = Encoding.GetEncoding(0);

The delimiter can also be changed (default is ',').

    CsvSerializer.Separator = '\t';

You can also use stream instead of file name.

    using (var stream = new FileStream(csvFileName, FileMode.Create))
        await collection.WriteCsvAsync(stream);

You can leave the stream open after reading or writing by specifying leaveOpen.

    using (var stream = new FileStream(csvFileName, FileMode.Create))
        await collection.WriteCsvAsync(stream: stream, bufferSize: 1024, leaveOpen:true, hasHeader: true);

In addition to asynchronous methods, there are also synchronous methods.

    toDoes.WriteCsv(csvFileName);

NuGet and GitHub

These libraries are open to NuGet and can be installed from Visual Studio.

Source code is available at:

The projects included are:

Shos.CsvHelper

  • CSV library
  • .NET Standard Library version
  • You can build a.NET Standard 1.3 and later
  • .NET Network 4.6 or higher, or.NET Core 1.1 or later for
  • NuGet packages that can be installed: NuGet Gallery | Shos.CsvHelper

Shos.CsvHelper.NetFramework

Shos.CsvHelperSample.NetCore

  • .NET Core Console applications that use the Shos.CsvHelper sample

Shos.CsvHelperSample.NetFramework

  • Using Shos.CsvHelper.NetFramework.NET Framework console application sample

最新の C#/.NET を使用するには (2019/09/25版)

※ 「.NET Core 3.0 正式版リリース」の続き。

先日 .NET Core 3.0 が正式にリリースされ、C# 8.0 が使えるようになった。
一方で、.NET Framework の最新版は 4.8 だ (2019/09/25 現在)。

現時点での、最新版の .NET を使用する方法をまとめてみよう。

.NET Framework 4.8 を使うには

下記からインストール。
※ Developer pack と Language pack の両方

.NET Core 3 で開発するには

Visual Studio Installer で Visual Studio 2019 Ver.16.3 以上にバージョンアップ

Blazor WebAssembly (クライアントサイド Blazor: プレビュー版) を試すには

最新版の Visual Studio に Blazor テンプレートのインストールが必要
次を参照

C# 8.0 で開発するには

プロジェクト ファイル (*.csproj など) に <LangVersion>8.0</LangVersion> を記述
(null 許容参照型も有効にするには <Nullable>enable</Nullable> も記述)

例.

  <?xml version="1.0" encoding="utf-8"?>
  <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      ...
      <PropertyGroup>
        ...
        <LangVersion>8.0</LangVersion>
        <Nullable>enable</Nullable>
        ...
    

 

.NET Core 3.0 アプリケーションのデフォルトの C# のバージョンは 8.0

参考資料

.NET Core 3.0 正式版リリース

.NET Core 3.0 正式版リリース

昨日 (2019/09/23)、.NET Core 3.0 正式版がリリースされた。

現在これに関連して、.NET Conf 2019 というオンライン カンファレンスが開催されている。
一部は、YouTube で見ることができる。

.NET Core 3.0

Microsoft 製の新たな .NET である .NET Core だが、.NET Core 3.0 では、Windows デスクトップ アプリケーション (Windows Forms と WPF) がサポートされた。

.NET Core 3.0 のためのソフトウェアの開発には、現在 Visual Studio 2019 の最新版である Ver.16.3.0 が必要だ。
Visual Studio Installer からアップデートできる。

但し、現在 .NET Core の Windows Forms のデザイナーには未対応であるようだ。

(なお .NET Core は、最初の .NET である .NET Framework に比べて様々な最適化がなされているため、より高パフォーマンスとなっている)

.NET Core の Roadmap

  • 2019/11 .NET Core 3.1 (LTS : Long Term Support 版)
  • 2020/11 .NET 5

.NET 5 は、.NET Core 3 の次期バージョンだ。

現在 3つある Microsoft .NET (.NET Framework, .NET Core, Xamarin) が 1 つに統合される予定。
これ以降、.NET Framework は保守のみとなり、新機能は追加されなくなる。

.NET Core 上の C++/CLI

C++/CLI .NET Core 3.1 でサポートされる予定だ。

Blazor

新たな ASP.NET (.NET での Web アプリケーション プラットフォーム) のフレームワークである Blazor だが、サーバーサイドの Blazor は、既に .NET Core 3.0 でサポートされている。

サーバーサイドの Blazor は、C# と Razor 構文でコーディングでき、クライアント サイドとの同期はフレームワークによって行われる (内部的には JavaScript を用いて SignalR で通信) という技術だ。

クライアント サイド Blazor (Blazor WebAssembly) は、WebAssembly で .NET が動作し、C# のアプリケーションがクライアント サイドの Web ブラウザ上で動作するというユニークなものだが、こちらは、現在まだ Preview 版。
2020年5月のリリースが予定されている。

C#

C#8.0 がリリースされた。
null 許容参照型や非同期ストリームなどがサポートされる。
非同期ストリームでは、非同期型の IEnumerable である IAsyncEnumerable が使われる。

関連記事

Microsoft MVP を再受賞しました (My 15th MVP Award from Microsoft)

MVP_Logo_Horizontal_Preferred_Cyan300_CMYK_72ppi.png

Microsoft MVP Award を再受賞しました。15年目になります。

My MVP Profile

いい歳の大人をこうして褒めてくれるマイクロソフトに感謝です。

Microsoft MVP となって、多くの素敵なITエンジニアの皆様との交流の機会が増えました。 皆様いつもありがとうございます。