コード スニペット テクノロジを精査する
Andrew W. Troelsen
Intertech Training
August 2004
概要 : この記事では、Visual Studio 2005 および Visual C# 2005 Express Edition のコード スニペット テクノロジの使用について検討します。まず、コード スニペットを表現するために使用される XML 構文について説明し、次に、選択した Microsoft .NET IDE で、カスタム コード展開を作成し、登録するプロセスについて説明します。
**メモ **この記事は、C# プログラミング言語および .NET プラットフォームについての知識があることを前提としています。XML に関する基本的な知識も役立ちます。
**適用対象 :
Microsoft Visual Studio 2005
Microsoft Visual C# 2005 Express Edition
目次
コード スニペットの役割
コード スニペットのカタログ
定義済みコード スニペット ファイルの検索
コード スニペットの内部
カスタム コード スニペットの作成
新しい型のインスタンス化の展開
新しい Web メソッドの展開
破棄型の展開
$selected$ の概要
カスタム コード スニペットの登録
まとめ
コード スニペットの役割
Visual Studio 2005 と Visual C# Express Edition は、どちらも "コード スニペット" と呼ばれるテクノロジをサポートしています。コード スニペットは、2 つの関連するコード生成技術の基礎をなすものです。
- Expansion テンプレート : 型定義、メンバ定義、および共通のコーディング構成要素を生成する IDE の機能
- Surround With IntelliSense : 関連するコーディング構成要素内の選択されたコード ステートメントのグループを囲む IDE の機能
コード スニペット テクノロジは、ただ 1 つの目的、すなわち開発者の生産性のために存在しているといえます。Expansion テンプレートおよび Surround With IntelliSense を使用すると、開発者は、次のいずれかのアプローチを使用してコード ブロックを迅速に生成できます。
- IDE の [編集] メニューの [IntelliSense]
- IDE 依存のキーボードのショートカット
- コンテキスト依存のマウスの右クリック
- スニペットの登録済みショートカットの入力 (スニペットの XML ファイルで定義)
Expansion テンプレートの例
Expansion テンプレートの役割を説明するために、ここで、C# クラス型 (SportsCar という名前) を作成済みで、関連するバッキング フィールドを持つプロパティを迅速に追加すると仮定します。手動でメンバ変数を宣言し、プロパティの構文を作成することなく、(上記の手法のいずれかを使用して) property
Expansion をアクティブにし、プロセス全体を自動化できます。
たとえば、型定義のスコープ内にマウス カーソルを置き、[編集] メニューの [IntelliSense]、[拡張の挿入] を順に選択します。表示される選択肢の一覧から、以下の図 1 に示されているように、[property] を選択します。
図 1. コード展開をアクティブにします。
このコード スニペットがアクティブになると、SportsCar 型内に次のプロパティおよびフィールド宣言が表示されます。
private int myVar;
public int MyProperty
{
get { return myVar; }
set { myVar = value; }
}
ご覧のように、展開は汎用的になるように意図されています。しかし、図 2 の展開をよく見ると、プロパティとフィールドそれぞれの名前と型が黄色の四角形で強調表示されていることがわかります。
図 2. 黄色の強調表示が、スケルトン展開を編集する手掛かりです。
これらの黄色の強調表示は、展開を完了させるために編集が必要なトークンを表します。Tab キーを使用して、強調表示された各アイテム間を移動し、適宜編集できます。
必要に応じて、展開のカスケードを編集します。フィールドの型 (既定では int
) に Tab キーで移動し、これを string
に変更すると、アイテムから Tab キーで離れたときに、プロパティの型宣言が自動的に更新されます。同様に、フィールド名 (既定では myVar
) に Tab キーで移動し、これを _carColor
に更新すると、関連するプロパティの get
および set
スコープは、図 3 に示されているように自動的に更新されます。
図 3. "property" コード展開の詳細のまとめ
Surround With IntelliSense の例
コード スニペット テクノロジのもう 1 つの例は、Surround With IntelliSense です。Expansion テンプレートと同様に、Surround With IntelliSense テクノロジもコードを生成します。ただし、その違いは、Surround With IntelliSense ベースのコード スニペットは、展開を適用する前に、ユーザーによるコード ステートメントのブロックの選択を許可することです。
説明上、ここで、C# の #region または #endregion ディレクティブを使用して、領域に SportsCar 型を置こうとしていると仮定します。まず、領域内でラップするコード ステートメントを選択し、[編集] メニューの [IntelliSense]、[ブロックを囲んで挿入] を順に選択します (図 4)。
図 4. Surround With IntelliSense の対象となるステートメントを選択します。
表示される一覧から、#region
を選択し、Tab キーによる入力候補を使用して、必要に応じて領域名を編集します。
メモ Surround With IntelliSense は、ステートメントのセットを選択しなくても、使用できます。この場合、コード スニペットは、単に、一語一句たがわず生成されます。
コード スニペットのカタログ
Visual Studio 2005 と Visual C# 2005 Express Edition は、同じ組み込みコード スニペットを定義しています。次の表は、共通の Surround With コード スニペットのいくつかを示しています。
Surround With スニペット | 定義 |
---|---|
#if
#region |
これらのオプションを使用すると、さまざまな C# プリプロセッサ ディレクティブでコードをラップできます。 |
do
while foreach for ? iteration by index Reverse for ? iteration by index |
これらのオプションで、さまざまな C# ループ構造内でコードをラップします。 |
if statement
else |
if/else ロジック内のコード ブロックをラップします。 |
lock | ロック スコープ内のコード ブロックをラップします。 |
namespace | 型のコレクション (名前空間) を新しい名前空間にラップします。 |
Try Catch
Try Finally |
構造化例外ロジック内のコード ブロックをラップします。 |
Using | C# "using scope" 内のステートメントをラップして、オブジェクトを確実に破棄します。 |
次の表は、Visual Studio 2005 および Visual C# 2005 Express Edition IDE によって提供される Expansion テンプレートのいくつかを示しています。
Expansion テンプレート スニペット | 定義 |
---|---|
class
interface enum struct |
これらのオプションは、空の型定義を生成します。 |
Named Iterator / Indexer pair | イテレータは、インデックス構文と同様の配列を使用して、サブオブジェクトを公開するカスタム コレクションの作成を可能にする構成要素です。 |
Basic attribute implementation | (ベスト プラクティスに従って) 新しいカスタム .NET 属性定義を作成します。 |
Destructor | C# デストラクタ (見かけはオーバーライドされた System.Object.Finalize) からスタブを作成します。 |
Exception type | (ベスト プラクティスに従って) カスタム例外用の定義を作成します。 |
Override System.Object.Equals | System.Object から継承された仮想 Equals() メソッドをオーバーライドします。 |
Property
Propertyg |
読み取りまたは書き込みのプロパティ (property) または読み取り専用のプロパティ定義 (propertyg) を定義します。 |
さて、予想に反するかもしれませんが、以降のセクションでは、各コード スニペットの使用方法については説明しません。各オプションについては、各自でいつでも調べることが可能だからです。
ここで検証したいのは、コード スニペットの内部構成についてです。これを説明した後、Visual Studio 2005 および Visual C# 2005 Express Edition で使用する、独自のカスタム コード スニペットの作成および登録方法について説明します。
定義済みコード スニペット ファイルの検索
.NET プラットフォームの経験が少しでもある方ならば、コード スニペットが XML を使用して表現されることは決して意外ではないでしょう (今日、XML として表されないものなどあるでしょうか)。各コード スニペットは、一意の *.xml ファイルに格納され、既定では次のパスの下にあります。
<drive>:\Program Files\Microsoft Visual Studio 8\VC#\ Expansions\1041\Expansions
メモ このパスは、Visual Studio 2005 および Visual C# 2005 Express Edition のベータ バージョンに基づいています。Expansions フォルダが上記の場所に見つからない場合は、お使いのコンピュータで「class.xml 」という名前のファイルを検索してください。このファイル名が変更されていなければ、問題はありません。
コード スニペットの内部
既定のコード スニペットを表す *.xml ファイルを見つけたところで、詳細を見てみましょう。class.xml ファイルを開いてください。このファイルは、新しいクラス定義を挿入します。
<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippet Format="1.0.0">
<Header>
<Title>class</Title>
<Shortcut>class</Shortcut>
<Description>Expansion snippet for class</Description>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
<SnippetType>SurroundsWith</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Declarations>
<Literal default="true">
<ID>name</ID>
<ToolTip>Class name</ToolTip>
<Default>MyClass</Default>
</Literal>
</Declarations>
<Code Language="csharp" Format="CData">
<![CDATA[class $name$
{
$selected$$end$
}]]>
</Code>
</Snippet>
</CodeSnippet>
すべてのコード スニペットのルート要素は、<CodeSnippet> と名付けられています。この要素のスコープ内に、2 つの主なサブ要素があります。<Header> と <Snippet> です。
<Header> 要素
<Header> 要素は、コード スニペット自体の基本的な特徴を説明するために使用されます。次の表は、<Header> の主なサブ要素を示しています。
<Header> のサブ要素 | 定義 |
---|---|
<Title> | コード スニペットの表示タイトル。 |
<Shortcut> | コード スニペットのショートカットを定義します。
IDE で、スニペットを選択するためにショートカット名を入力し、Tab キーを押します。ショートカット名の一部を入力した場合 (たとえば、「class」ではなく「cla」)、Tab キーを 2 回押す必要があります。1 回は名前付き展開の入力を完了し、もう 1 回はスニペットを挿入するためです。 |
<Description> | 人間が読み取れる形式の、スニペットの説明です。IDE からスニペットを選択するときに表示されます。 |
<SnippetType> | コード スニペットが属するカテゴリ (Expansion、SurroundsWith、または Refactoring) を指定します。単一のコード スニペットが、複数のグループに属する場合があることに注意してください。
Microsoft IDE は、この値を使用して、コード スニペットの表示に使用するコンテキスト メニューを決定します。 |
これらのサブ要素が Visual Studio 内でどのように使用されるかを理解するために、#region コード スニペットをアクティブにしてください。図 5 内で、<Title>、<Description>、および <SnippetType> の値がどこに実現されているかを確認してください (ヒントが必要な場合は、関連する pp_region.xml ファイルを開いてください)。
図 5. 実行中の <Title>、<Description>、および <SnippetType> 要素
メモ コード スニペット テクノロジは、Microsoft IDE 内でリファクタリングをサポートするための基礎です。リファクタリング テクノロジの詳細については、この記事を参照してください。
<Snippet> 要素
<Snippet> サブ要素は、<Header> より少し複雑です。これは、実際にアクションが実行される場所であるためです。要約すると、<Snippet> 要素は、次の情報を定義します。
- 展開によって生成される "変数" のセット。これらは、この記事の前半で説明した、黄色で強調表示され、Tab キーで移動するトークンです。
- 展開がアクティブにされたときに生成される C# スケルトン コード
<Snippet> 要素は、展開変数を記述するために <Declarations> 要素を使用します。各展開変数は、一意の <Literal> 要素によって表されます。class.xml コード スニペットをもう 1 度例に取りましょう。この展開は、クラス名を表す単一の <Literal> のみを必要とします。
<Declarations>
<Literal default="true">
<ID>name</ID>
<ToolTip>Class name</ToolTip>
<Default>MyClass</Default>
</Literal>
</Declarations>
次の表は、<Literal> ノード内で使用される主なサブ要素を示しています。
<Literal> のサブ要素 | 定義 |
---|---|
<ID> | この値は、コード展開内の変数を識別するために使用される名前を表します。 |
<ToolTip> | IDE は、この値を使用して、黄色で強調表示された変数がマウス カーソルでポイントされたときにツール ヒントを表示します。 |
<Default> | コード スニペット変数の既定値を指定します。 |
ここで、これらの要素と IDE の対応を確認するために、クラス スニペットの展開の結果を示している図 6 を見てみましょう。
図 6. 実行中の <ToolTip> および <Default> 要素
<Code> 要素
最後に、<Snippet> 内で定義される <Code> サブ要素の役割について説明します。この要素は、挿入されるコードを定義するために使用され、CDATA セクションによって表されます (CDATA セクションは、XML ドキュメントに必要な、整形式の XML ではない定義済みデータに使用されることを思い出してください)。 以下に、class.xml コード スニペットの <Code> 要素を示します。
<Code Language="csharp" Format="CData">
<![CDATA[class $name$
{
$selected$$end$
}]]>
</Code>
さまざまな名前が $
トークンによって挟まれていることに注意してください (たとえば $name$
)。この構文は、<Literal> 要素内で定義された変数、および Visual Studio 2005 と Visual C# 2005 Express Edition によって解釈される定義済みリテラル (たとえば $selected$
) を参照するために使用されます。
また、$name$
トークンが、class.xml ドキュメントの <Declarations> セクションで定義された name
<Literal> を参照していることにも注意してください。$selected$
トークンは、展開がアクティブにされる前に、IDE 内で選択されたコード ステートメントを表します。また、おわかりのように、$end$
は、スニペットが展開された後に、ユーザーのカーソルを置く場所をマークするために使用されます。
カスタム コード スニペットの作成
XML にかなり慣れた方ならば、上記のセクションの説明だけで、カスタム コード スニペットを定義できるでしょう (もちろん、カスタム コード スニペットを Visual Studio に登録する方法についてはまだ説明していませんので、引き続き読み進めてください)。しかし、具体的な例を見たい方のために、日々の生産性を高めると思われるカスタム スニペットの例をいくつか紹介します。
- 新しい型のインスタンス化の展開
- 新しい Web メソッドの展開
- 破棄型の展開
これらのコード スニペットをすぐに利用する場合は、次の XML ドキュメントをコピーして、適切な名前の付いた *.xml ファイルに貼り付けてください。この記事の最後のセクションで、カスタム コード スニペットを Visual Studio 2005 および Visual C# 2005 Express Edition IDE に登録する方法について説明します。
新しい型のインスタンス化の展開
最初に作成するコード スニペット、newobj は、次の C# ステートメント (擬似コードで表示) を生成します。
[typeName] [varName] = new [typeName]();
[typeName] プレースホルダは、<Literal>、typename によって制御され、既定値 object
を持ちます。[varName] プレースホルダは、別の <Literal>、varname によって制御され、既定値 "obj" を持ちます。したがって、このコード スニペットが適用されると、図 7 に示されているような展開が表示されます。
図 7. 実行中の newobj コード スニペット
新しい XML ドキュメント (newobj.xml) を作成し、以下を入力してください。
<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippet Format="1.0.0">
<Header>
<Title>new object</Title>
<Shortcut>newobj</Shortcut>
<Description>Expansion snippet to allocate a new type
</Description>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Declarations>
<Literal>
<ID>typename</ID>
<ToolTip>type name</ToolTip>
<Default>object</Default>
</Literal>
<Literal>
<ID>varname</ID>
<ToolTip>variable name</ToolTip>
<Default>obj</Default>
</Literal>
</Declarations>
<Code Language="csharp" Format="CData">
<![CDATA[$typename$ $varname$ = new $typename$ ($end$);]]>
</Code>
</Snippet>
</CodeSnippet>
現在のバージョンの newobj.xml は、型の既定 (引数なし) コンストラクタをトリガするコードを生成します。拡張の一例としては、C スタイルのコード コメントを表す 3 つめの <Literal> を追加することが可能です。
<Literal>
<ID>ctorArgs</ID>
<ToolTip>Type in your ctor args</ToolTip>
<Default>/* Type Args Here */</Default>
</Literal>
CDATA セクションは、以下のように更新できます。
<Code Language="csharp" Format="CData">
<![CDATA[$typename$ $varname$ = new $typename$ ($ctorArgs$ $end$);]]>
</Code>
その結果、図 8 に示されているように、コンストラクタの引数を手動で入力できる、Tab キーで移動できるプレースホルダが生成されます。
図 8. 改善された newobj コード スニペット
新しい Web メソッドの展開
.NET プラットフォームを使用して XML Web サービスを構築する場合、強力な属性、[WebMethod] で、困難な処理のほとんどを実行できます。ご存じのように、[WebMethod] で修飾された各パブリック メソッドには、HTTP 要求を使用して到達でき、値は XML データ表現として返されます。最も単純な形式の場合、[WebMethod] は引数を取りません。
[WebMethod]
public SportsCar GetNewCar()
{
return new SportsCar();
}
しかし、[WebMethod] 属性は、いくつかのオプションの名前付きパラメータを取ることができます。そのうちの 1 つが Description です。
[WebMethod(Description = "This method returns a new SportsCar"]
public SportsCar GetNewCar()
{
return new SportsCar();
}
このため、次のコード スニペット、newWM では、Description 名前付きパラメータ、およびメソッドの戻り値と名前を表す <Literal> 要素が記述されています。 図 9 は、実行中の newWM を示しています。
図 9. 実行中の newWM コード スニペット
前のコード スニペットの背後にあるロジックを理解されたならば、newwebmethod.xml の内容は複雑ではないでしょう。
<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippet Format="1.0.0">
<Header>
<Title>new web method</Title>
<Shortcut>newWM</Shortcut>
<Description>Expansion snippet to generate a new Web Method
</Description>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Declarations>
<Literal>
<ID>retval</ID>
<ToolTip>return value</ToolTip>
<Default>void</Default>
</Literal>
<Literal>
<ID>methodname</ID>
<ToolTip>method name</ToolTip>
<Default>MyWebMethod</Default>
</Literal>
<Literal>
<ID>desc</ID>
<ToolTip>Description of Web Method</ToolTip>
<Default>This is my Web Method</Default>
</Literal>
</Declarations>
<Code Language="csharp" Format="CData">
<![CDATA[
[WebMethod(Description="$desc$")]
public $retval$ $methodname$()
{
$end$
}
]]>
</Code>
</Snippet>
</CodeSnippet>
メモ newWM 展開によって生成されるソース コードをコンパイルするには、プロジェクトは System.Web.Services.dll アセンSystem.Web.Services.dll アセンブリを参照する必要があります。また、C# ソース コード ファイルでは、System.Web.Services 名前空間を使用していることを指定する必要があります。
破棄型の展開
コード スニペットは、必要なだけコードに展開できます。たとえば、Exception Type 展開は、.NET のベスト プラクティスに準拠した、System.ApplicationException から派生するクラス全体を生成します。Basic attribute implementation コード スニペットは、System.Attribute から派生する新しい型に展開されます (これも、.NET のベスト プラクティスに従って構築されます)。
同じ文脈で、最後に破棄型を作成する Expansion テンプレートの作成を検証してみましょう。ご存じのように、アンマネージ リソースと対話する型を作成する場合、ベスト プラクティスでは以下のようにすべきとされています。
- **IDisposable インターフェイスを実装する。**オブジェクトのユーザーは、(理想的には) 定義済み Dispose() メソッドを呼び出して、すべてのアンマネージ リソースを解放する必要があります。
- **System.Object.Finalize をオーバーライドする。**オブジェクトのユーザーが Dispose() メソッドの呼び出しを忘れた場合、CLR ガベージ コレクタが (最終的に) 型のデストラクタを呼び出します。
dispclass コード スニペットは、図 10 にアクティブな状態が示されているように、そのような型を作成します。
図 10. 実行中の dispclass コード スニペット
以下はその XML です。もはやコメントは不要でしょう。
<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippet Format="1.0.0">
<Header>
<Title>disposable class</Title>
<Shortcut>dispclass</Shortcut>
<Description>Expansion snippet for a disposable class
</Description>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Declarations>
<Literal>
<ID>classname</ID>
<ToolTip>Class name</ToolTip>
<Default>MyResourceWrapper</Default>
</Literal>
</Declarations>
<Code Language="csharp" Format="CData">
<![CDATA[class $classname$ : IDisposable
{
~$classname$()
{
CleanUp(false);
}
public void Dispose()
{
CleanUp(true);
GC.SuppressFinalize(this);
}
protected void CleanUp(bool disposing)
{
if (disposing)
{
// 別の状態を解放します (マネージ オブジェクト)。
}
// 独自の状態を解放します (アンマネージ オブジェクト)。
// 大きなフィールドを NULL に設定します。
}
}]]>
</Code>
</Snippet>
</CodeSnippet>
$selected$ の概要
ここまで作成した各展開は、Expansion スニペット カテゴリに属します。
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
</SnippetTypes>
<SnippetType> 要素が、(Expansion ではなく) SurroundsWith に設定されている場合、コード スニペットは、Surround With コンテキスト メニュー内に表示されます。Surround With コード スニペットは、通常、コード エディタで選択されたステートメントを表す、$selected$
という名前の事前に定義されたリテラルを利用することを思い出してください。
説明のために、定義スコープ内で選択されたコード ステートメントをラップする $selected$
トークンを使用している簡単な例を示します (このスニペットは、Expansion および SurroundsWith カテゴリに属することに注意してください)。
<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippet Format="1.0.0">
<Header>
<Title>dummy scope</Title>
<Shortcut>dscope</Shortcut>
<Description>Surrounds selected code within a dummy scope
</Description>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
<SnippetType>SurroundsWith</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Code Language="csharp" Format="CData">
<![CDATA[
{
$selected$ $end$
}]]>
</Code>
</Snippet>
</CodeSnippet>
繰り返しになりますが、ダミー スコープ内でコードをグルーピングしても得るものはあまりないため、この例はあくまで説明のために使用します。ただし、カスタム コード スニペットを作成している場合、$selected$
トークンを使用すれば、IDE 内の現在の選択をキャプチャできることを覚えておいてください。
カスタム コード スニペットの登録
さて、一連のカスタム コード スニペットが準備できたところで、これらの存在を Visual Studio にアドバタイズする必要があります。幸いなことに、Visual Studio 2005 と Visual C# 2005 Express Edition では、これを実行するためのアプローチは共通です。
まず、図 11 に示されているように、作成した *.xml ファイルを、設定なしで使用できるコード展開ファイルを含むディレクトリに単純にコピーします。
図 11. 既定の Expansions フォルダにカスタム コード スニペットを登録します。
このアプローチに従うと、図 12 に示されているように、作成したカスタム コード スニペットが、関連するコンテキスト メニュー内にアルファベット順で表示されます。
図 12. IntelliSense を使用して作成したコード スニペットにアクセスします。
もう 1 つのアプローチとしては、作成したコード スニペット用に特定のフォルダ (たとえば、C:\MySnips) を作成し、[ツール] メニューの [コード スニペット マネージャ] を選択して、この場所を登録します。一度アクティブにすると、図 13 のようなダイアログ ボックスが表示され、作成した XML ドキュメントを含む別のフォルダを ([追加] ボタンを使用して) 指定できるようになります。
図 13. カスタム スニペットの場所を登録します。
メモ 図 13 からおわかりのように、<Header> 要素には、スニペットの作成者を説明する <Author> サブ要素を含めることができます。
このアプローチでは、図 14 に示されているように、IDE のコンテキスト メニューからフォルダを選択する必要があります。
図 14. カスタムの場所を選択します。
実行すると、作成したスニペットが期待どおりに表示されます。
図 15. (カスタムの場所にある) カスタム スニペットを選択します。
ここまでで、基本を理解され、必要とするあらゆるコード展開を作成できるようになられたことでしょう。論理的な次のステップとして、お使いの Microsoft IDE に付属するスニペットを調べて、理解を深めてください。それでは、楽しいコーディングを。
まとめ
Visual Studio 2005 と Visual C# 2005 Express Edition には、それぞれ、事前定義された一連のコード スニペットが付属しています。スニペットは、小さな XML 要素セットを使用して作成され、共通タスク用のコードを迅速に生成するために使用されます。付属のコード スニペットを利用することに加えて、開発者は、登録すると IDE によって処理される、カスタム Surround With および Expansion テンプレートを自由に作成できます。
Andrew Troelsen は、Intertech Training のコンサルタントでありトレーナーでもあります。Andrew には、賞を受けた『C# and the .NET Platform Second Edition』 (2002 年 Apress) を含む多数の著作があります。また、SSCLI、Portable.NET、および Mono CLI 配布を使用した、Unix ベースのシステムでの .NET 開発について探った、(こともあろうに) MacTech
の月次コラムも執筆しました。