暗黙的・明示的型変換を実現するimplicitとexplicitの特徴と使用方法

# 最初に

本記事は`implicit`と`explicit`の違いと使い方に対して解説したものとなります。

また本記事は一度昔に個人ブログにあげていたものに修正を加えて再掲したものとなります。


&nbsp;
<h1 id="implicitとexplicitについて-型変換演算子(ConversionOperator)とは"><strong>型変換演算子(Conversion Operator)とは</strong></h1>

**ユーザー定義型を組み込み型(int等)にキャストできるようにしたい時**に使用します。逆に組み込み型をユーザー定義型にキャストすることも可能にできます。C#には**暗黙の型変換を可能にする`implicitキーワード`と、明示的な型変換を可能にする`explicitキーワード`**が存在します。注意として、これらのキーワードを使用する際は、**すべてstatic宣言が必要**となります。

<!--more-->
<h3>ユーザー定義型と組み込み型</h3>
<table class="confluenceTable tablesorter">
<thead>
<tr class="sortableHeader">
<th class="confluenceTh sortableHeader" data-column="0"></th>
<th class="confluenceTh sortableHeader" data-column="1">
<div class="tablesorter-header-inner">組み込み型</div></th>
<th class="confluenceTh sortableHeader" data-column="2">
<div class="tablesorter-header-inner">ユーザー定義型</div></th>
<th class="confluenceTh sortableHeader" data-column="3">
<div class="tablesorter-header-inner">他の型から合成する型</div></th>
</tr>
</thead>
<tbody>
<tr>
<td class="confluenceTd">値型</td>
<td class="confluenceTd">単純型</td>
<td class="confluenceTd">構造体

列挙型</td>
<td class="confluenceTd">Null許容型</td>
</tr>
<tr>
<td class="confluenceTd">参照型</td>
<td class="confluenceTd">文字列型

オブジェクト型</td>
<td class="confluenceTd">クラス

インターフェイス

デリゲート</td>
<td class="confluenceTd">配列</td>
</tr>
<tr>
<td class="confluenceTd">その他</td>
<td class="confluenceTd"></td>
<td class="confluenceTd"></td>
<td class="confluenceTd">ポインター</td>
</tr>
</tbody>
</table>
(++C++; // 未確認飛行C　様サイトより)

<a href="http://ufcpp.net/study/csharp/st_embeddedtype.html" target="_blank">組み込み型</a>

- 値型：値をスタック上に領域を確保する。コピー渡し。継承不可。
- 参照型：値をヒープ上に領域を確保する。参照渡し(型安全なポインタ)。
- 組み込み型：C#に予め、組み込まれている変数の型(クラス)。
- ユーザー定義型：プログラマが自由に作ることができる型。
- 他の型から合成する型：ある型<code>T</code>から<code>T[]</code>や<code>T?</code>などというような書き方で「合成」して作る型。

### 暗黙的・明示的な変換

```C#
int hoge = 0;
double huga = 0d;
 
// 暗黙的な変換
huga = hoge;
 
// 明示的な変換
hoge = (int)huga;
```

## どのような場合に使用するのか

自作のクラスから別のクラスへの変換機能を使用する必要があった場合、自前でstaticメソッドを追加するのがひとつの方法ですが、変換する必要がある度にメソッド呼び出しを書かなければなりません。そこで`int -> double`へ自動的に型変換が行えるように、**自作クラスと別クラスが相互に代入**できればもっとコードを簡潔に書くことができるようになります。

### 注意点

- 組み込み型から組み込み型への変換は作成できない。
- 継承元である基本クラスへの変換、または基本クラスから派生クラスへの変換を行う変換演算子を定義できないため、**object型を伴う変換は定義することができない**。
- 特定の変換元と変換後の型の組み合わせに対して、暗黙的な変換と明示的な変換の両方を定義することはできない。
- **基本クラスから派生クラスへの変換(その逆も然り)は定義することができない**。
- インターフェイスを伴う変換は定義することはできない。
- Effective C#では推奨されていない。ユーザー定義型に変換演算子を定義した場合、**コンパイラは、その型が対象の型としても扱えると認識するようになる**。しかし独自の型が対象の型と完全に同じ機能をサポートしてるわけではないので、**場合によっては潜在的なエラーを引き起こす**。また、変換演算子が一時オブジェクトを返すような場合には、**一時オブジェクトのみが変更され、元のオブジェクトには変更が反映されないままガベージコレクタの対象となる**。そして、変換演算子の呼び出し規則は実行時の型ではなく、コンパイル時の型に基づく。型を使用する場合に、**キャストを複数回行わなければ変換演算子を呼び出せないような状態**だと、そのコードは実質的に**メンテナンス不可能な状態**となる。独自に作成した型を別の型に変換したい場合は、**コンストラクタを使用する方が可読性に優れ、問題を引き起こす可能性も低い**。

以上のことから、`implicit`と`explicit`の扱いには充分注意してください。

# implicit

**暗黙的なユーザー定義型変換演算子を定義する**のに使用するキーワードです。そのため、メソッドにオブジェクトを引数として渡したり、定義した型に代入する時、**ある型を別の型として扱う必要がある場合に常に呼ばれます**。便利ではありますが、**変換時に情報が失われず(丸め誤差、オーバーフロー等)、例外が発生しない場合に限り**使用しましょう。`implicit`キーワードを用いることで、不要なキャストを省けるのでコードが読みやすくなります。

以下使用例：

```C#
public class Hoge
{
    public int x;
 
    public Hoge(int _x) { x = _x; }
 
    public static implicit operator int(Hoge hoge)
    {
        return hoge.x;
    }
 
    public static implicit operator Hoge(int _x)
    {
        return new Hoge(_x);
    }
}
 
class MainProgram
{
    static void Main(string[] args)
    {
        Hoge piyo = new Hoge(10);
        int num = piyo;
        Hoge piyo2 = 12;
        Console.WriteLine("num = {0} piyo2 = {1}", num, piyo2.x);
    }
}
 
/////////////////
出力結果
num = 10 piyo = 12
////////////////
```

&nbsp;
# explicit

**明示的なユーザー定義型変換演算子**を定義します。暗黙的変換演算子である`implicit`とは異なり、**キャストが明示的に指定された場合以外は呼び出されることはありません**。そのため、**コードを読む全ての人に明確にキャストを示すことができます**。キャストを省くと`コンパイルエラー CS0266`となるのでエラー個所もわかりやすいです。

以下使用例：

```C#
public class Hoge
{
    public int x;
 
    public Hoge(int _x) { x = _x; }
 
    public static explicit operator int(Hoge hoge)
    {
        return hoge.x;
    }
 
    public static explicit operator Hoge(int _x)
    {
        return new Hoge(_x);
    }
}
 
class MainProgram
{
    static void Main(string[] args)
    {
        Hoge piyo = new Hoge(10);
        int num = (int)piyo;
        Hoge piyo2 = (Hoge)12;
        Console.WriteLine("num = {0} piyo2 = {1}", num, piyo2.x);
    }
}
 
/////////////////
出力結果
num = 10 piyo = 12
////////////////
```


# 参考

- [演算子のオーバーロード ++C++; // 未確認飛行C](https://ufcpp.net/study/csharp/oo_operator.html)