C# 値オブジェクトの基底クラス

.Net7 から追加された INumber<T> インターフェースを用いて、
値オブジェクトの基底クラスとなるような record クラスを作成しました。
INumber<T> 型を使用するジェネリッククラスとすることで、
int や double などを場合分けせずに記述することが可能となります。

プリミティブな値型と同じような感覚で扱え、
重要なドメインルールを持つクラスを簡単に作成できるようにすることが目的です。

/// <summary>値の基底クラス</summary>
public abstract record ValueBase<TValue, TSelf>
    where TValue : INumber<TValue>
    where TSelf : ValueBase<TValue, TSelf> {

    private static readonly Func<TValue, TSelf> CreateObject;

    /// <summary>値</summary>
    public TValue Value { get; }

    /// <summary>静的コンストラクタ</summary>
    static ValueBase() {
        var constructorInfo = typeof(TSelf).GetConstructor([typeof(TValue)])
                           ?? throw new NotSupportedException();
        CreateObject = v => (TSelf)constructorInfo.Invoke([v]);
    }

    /// <summary>コンストラクタ</summary>
    protected ValueBase(TValue value) {
        Value = value;
    }

    /// <summary>暗黙的型変換</summary>
    public static implicit operator TValue(ValueBase<TValue, TSelf> valueObject) {
        return valueObject.Value;
    }

    /// <summary>+オペレーター</summary>
    public static TSelf operator +(ValueBase<TValue, TSelf> lhs, ValueBase<TValue, TSelf> rhs) {
        return CreateObject(lhs.Value + rhs.Value);
    }

    /// <summary>ToString</summary>
    public sealed override string ToString() {
        return Value.ToString()!;
    }
}

静的コンストラクタでは、リフレクションを用いてTValue型を引数にとり
継承先のオブジェクトを生成する関数を作成しています。

この例では四則演算のうち加算のみ実装しています。
値オブジェクトから数値への暗黙変換を許可しています。
またrecord 型の継承先クラスではコンパイラにより ToString() が自動でオーバーライドされるので、
数値のみが出力されるように ToString() を sealed にしています。

使用する側は以下のようにします。

/// <summary>得点クラス</summary>
public sealed record Score : ValueBase<int, Score> {
    public Score(int value) : base(value) {}
}

// 使用例
Score score10 = new Score(10);
Score score20 = new Score(20);
Score total = score10 + score20;
Console.WriteLine(total);    // 30 が出力される

 


					

C# using文でオブジェクト初期化子を使用すると警告される

C#でusing文やusing変数宣言(C#8.0~)のオブジェクトに対して、
オブジェクト初期化子を使用すると警告がでます。
以下の画像で、zipFile2の方はnewの部分が波線で警告されているのが分かります。

これはオブジェクト初期化子の処理中で例外が発生した場合に、
オブジェクトが正しく Dispose されないことが原因です。
Low-level なコードで表すと、オブジェクト初期化処理はtry文の外にあり、
この場合はコンストラクタで例外が発生した場合と同様に、
Dispose が呼ばれないことが確認できます。

オブジェクトの初期化に関しては、
一般的にオブジェクト初期化子を利用するのが良いと言われています。
ただし、using文ではその限りではないことが分かりました。

また IDisposable を継承するクラスに関しても、
プロパティ設定で例外が発生するような設計は避けるべきです。
ただ設計によっては record型の値オブジェクト等のコンストラクタでは、
例外を出すことも珍しくない為、usingのオブジェクト初期化子には注意が必要です。

開発用PC更新

オフィスではデスクトップPCを使って開発を行っています。
先日、約4年間使用したPCを更新しました。
好きなPCを選んで良いとのことでしたので、Intel Core i9-14900 の高性能なPCにし、
開発がとても快適になりました。

4年前の前回の更新時はCPUにAMD Ryzenを選び、
SSDはそれまでのPCで使用していたHDDをクローンして使用しました。
今回はSSDのクローンが上手くいかなかった為、OSのインストールから行いました。

開発用PCには、新旧様々な開発環境やアプリケーションとその設定、ドライバーなどが入っており、
新規にインストールして開発環境を一から構築するのは骨の折れる作業です。
特に古い開発環境は素直にインストールできなかったりする場合もあります。

ただその甲斐もあって、CPU, SSD, メモリなどの世代が新しくなった事や、
OSクリーンインストールを行ったことで、全体的にきびきび動作するようになり、快適です。
開発中のソフトウェアのビルド時間も大幅に短縮され、効率的にもなりました。
気分も一新して、開発に挑みたいと思います。

Xamarinのサポート終了

弊社でも使用しているMicrosoft Xamarinのサポートが間もなく終了します。
iOS関連と合わせて開発環境の状況を整理しました。

Microsoft関連
- 2024/5/1:Xamarin.Android サポート終了
- 2024/5/1:Xamarin.Forms サポート終了
- 2024/5/1:Xamarin.iOS サポート終了
- 2024/8/31:Visual Studio for Mac廃止

iOS関連
- 2024/4/29:ストアへのアプリは、Xcode 15 と iOS 17 SDKが必要

既存のiOSアプリは、ストアがXcode 15が受け入れられるまではアップデートは可能と思われます。
Visual Studio for Mac廃止に伴い、
MacでのC#開発用IDEの代替としてはVisual Studio Code、
または仮想Windows上のVisual Studioが位置づけされているようです。
MacでUnity開発などを行っている場合は大きな影響がありそうです。

弊社でも今後はモバイルアプリは.NET MAUIに移行していきます。

Visual Studioの便利機能

C#の開発はVisual Studio 2022で行っています。
VSは機能追加のアップデートも頻繁に行われており、便利な機能も多くあります。
ここでは「クイック ウォッチ」と「IEnumerable Visualizer」を紹介します。

実行中にブレイクポイントで停止させて、
オブジェクトの状態を確認するシーンは数多くあると思います。
どのようなIDEでも変数やオブジェクトをウォッチする機能はありますが、
VSでは変数やオブジェクトを右クリックして「クイック ウォッチ」があります。
通常のウォッチと同じような専用の画面が表示され、特に多くの情報を一覧で見たい場合に有効です。

「IEnumerable Visualizer」はIEnumerableな変数にカーソルを合わせると表示される、
「表示」から選択できます。
匿名クラスで欲しい情報を集めてIEnumerableな変数にしておけば、
その情報をCSVかExcel形式で出力することもできるので、
デバッグやデータをExcelで検証する場合などに重宝しています。

関連を表す矢印には気を付ける

我々は物事の概要を伝える場合に、よく概念図を利用します。
例えばシステム全体図、サーバーとクライアントの関係、クラスAとクラスBの関連などです。
この時関連を表すのに矢印をよく使いますが、図を描くときにも読むときにもとても注意が必要です。

例えばクラス図においては、
A→B はAがBを知っている場合に使いますが、その逆はありません。
同じようにして、
例えばモデルと画面の分離アーキテクチャMVVMを表現すると、V→VM→M となります。
上の2つの例は参照と言い換えることができます。

MVVMを説明する図で V⇆VM⇆M と表現されている場合もあります。
この場合は単にVとMが直接繋がっていないことを強調している場合もあれば、
単純なデータの流れを表しているとも考えられるし、
イベントまで考えると、VはVMを知っているし、VMはイベント発行側なのでVへの参照を持つので、
これもやはり参照を表した矢印と考えることもできます。

大切なのは何を表現しようとした矢印なのかをしっかり考え、
1つの図の中で、参照とデータのような意味の異なる矢印を使わないようにすることです。

画面のデザインは難しい

2024年 本年もどうぞよろしくお願い申し上げます。

先日Windowsのソフトで画面の実装を行う機会があり、
それほど複雑な画面ではないものの、ユーザーにとって視認性の高い画面にする必要がありました。

仕様書の段階で画面のイメージは概ね出来上がっていたものの、
実装段階になるとデザインがなかなか上手くできずに困り、
結局、この件ではデザイナの方にデザインをお願いすることになりました。

WPFやMVVMなどのフレームワークが整っている環境でも、
実際にはデザイナではない開発者が、画面のデザインや実装も担当する場合が多いと思います。
その場合でもデザイナの方が行った目標とするデザイン(ゴール)がきちんとあれば、
実装はとてもやりやすいと感じました。

マイコン開発でのリンカ設定

C言語でマイコン開発を行っている中で、関数外で宣言した変数の値が書き換えられない現象に遭遇しました。

static bool _initializedA;           // A
static bool _initializedB = false;   // B

void init() {
  _initializedA = true;
  _initializedB = true;
}

上のようなコードで、Aは関数内で書き換え可能ですが、Bは書き換えされず、関数を抜けても _initializedB の値が false のままという内容でした。コンパイルおよび実行時にも警告やエラーが発生せず、最初理由がよく分かりませんでした。

原因はリンカの設定によるものでした。Bのほうは初期値ありなので.dataセクション(ROM)に配置され、リンカのデフォルトの設定ではROMからRAMへのマップが行われていませんでした。

実はこのときは普段使用しているものとは異なるマイコンでの開発であったため、開発環境も普段とは異なり、デフォルトのリンカの設定がこのようになっているとは知りませんでした。

C言語での開発はオープン系言語と比べると、このような点でも難しく感じます。せめて警告やエラーが発生すればもっと早く気が付けるのですが、言語知識だけでなく、経験や経験に基づく感も必要なようです。

初めてのCとC++

これまでの業務ではC#を使ってきました。
今回初めてCとC++に携わる機会があり、学習しました。
せっかくなのでModern C, C++ と言われる比較的新しいバージョンです。

C#を使い慣れているとCやC++ではロジックのコード量が増え、
こんなこともできないのか、と思う一方、
特にメモリの扱われ方などはより理解が深まりました。

ただC++は言語仕様としてもかなり複雑で、Cの次に学ぶにはとても難しい印象です。
なのでC++習得するには先にC#等を理解して、オブジェクト指向の特徴や、
最近の言語の機能、プログラミングスタイルなどを知った後のほうが、
C++を使ってどのような設計や関数を目指すべきかということがよく分かると思います。

RustもC++とよく比較されて人気があるようなので、
今後触れてみたいと思います。

初級ソフトウェア品質技術者 資格試験

11月に、初級ソフトウェア品質技術者 資格試験を受けました。
ソフトウェアの品質技術を身につけ、品質の向上を目的とした試験で、
ソフトウェアの品質向上に関する知識を体系的に学ぶ為に受験しました。

試験に関しては、初級試験の合格率は40%前後なので、きちんと学習しないと合格は難しいようです。
また今回の試験から新しいシラバス(Ver.3.0)となっており、出題範囲も広くなっています。
私の場合はシラバスの用語をピックアップしながら、テキストを読み進めるスタイルで学習しました。
実務で品質を扱っていないと用語や概念が難しい部分もありますが、
ソフトウェアのライフサイクル(企画~廃棄まで)を意識して、
今はどのプロセスの内容の事を指しているのかを把握する事が重要だと感じました。

学習した内容を生かして、
今後は社内のプロセス改善や品質向上に役立てていきたいと思います。