ユーザー定義変換
暗黙的変換演算子と明示的変換演算子を追加します。
「ユーザー定義変換」はCoddyKit上の無料C# Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC# Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C# Academyコースには全4レッスンが含まれています。
ユーザー定義変換
C# では、implicit 演算子と explicit 演算子を使って、型が別の型との間でどのように変換されるかを定義できます。暗黙的な変換は自動的に行われ、明示的な変換にはキャストが必要です。
暗黙的な変換
変換が常に安全で情報を失わない場合は、implicit operator を使用します。コンパイラーは、変換先の型が必要とされる場所で自動的に適用します。
using System;
public struct Celsius
{
public double Degrees;
public Celsius(double d) { Degrees = d; }
// Always safe: a Celsius is just a number
public static implicit operator double(Celsius c) => c.Degrees;
}
public class Program
{
public static void Main()
{
Celsius temp = new Celsius(21.5);
double d = temp; // implicit, no cast needed
Console.WriteLine(d);
}
}明示的な変換
変換によって情報が失われたり失敗したりする可能性がある場合は、explicit operator を使用します。呼び出し側はキャストを記述する必要があり、リスクを受け入れることを明示します。
using System;
public struct Money
{
public decimal Amount;
public Money(decimal a) { Amount = a; }
// Loses the fractional cents, so make it explicit
public static explicit operator int(Money m) => (int)m.Amount;
}
public class Program
{
public static void Main()
{
var m = new Money(19.99m);
int dollars = (int)m; // explicit cast required
Console.WriteLine(dollars);
}
}独自の型への変換
逆方向の変換、つまり別の型から独自の型への変換も定義できます。ここでは、implicit 演算子によって、元の double から Celsius を作成します。
using System;
public struct Celsius
{
public double Degrees;
public Celsius(double d) { Degrees = d; }
public static implicit operator Celsius(double d) => new Celsius(d);
public override string ToString() => Degrees + "C";
}
public class Program
{
public static void Main()
{
Celsius t = 18.0; // double implicitly becomes Celsius
Console.WriteLine(t);
}
}暗黙的変換と明示的変換:使い分け
経験則として、データが失われず、例外も発生しない場合に限って暗黙的変換にします。精度が失われたり、オーバーフローしたり、失敗したりする可能性がある場合は、明示的変換にします。
using System;
public struct Percentage
{
public double Value; // 0..1
public Percentage(double v) { Value = v; }
// Safe and lossless -> implicit
public static implicit operator double(Percentage p) => p.Value;
// Could be out of range -> explicit
public static explicit operator Percentage(double v) => new Percentage(v);
}
public class Program
{
public static void Main()
{
double d = new Percentage(0.25);
var p = (Percentage)0.5;
Console.WriteLine(d + " " + p.Value);
}
}カスタム型間の変換
変換は組み込み型に限られません。たとえば極座標から直交座標へ変換するように、自分で定義した2つの型の間でも変換できます。
using System;
public struct Cartesian
{
public double X, Y;
public Cartesian(double x, double y) { X = x; Y = y; }
public override string ToString() => "(" + Math.Round(X, 2) + ", " + Math.Round(Y, 2) + ")";
}
public struct Polar
{
public double R, Theta;
public Polar(double r, double theta) { R = r; Theta = theta; }
public static explicit operator Cartesian(Polar p)
=> new Cartesian(p.R * Math.Cos(p.Theta), p.R * Math.Sin(p.Theta));
}
public class Program
{
public static void Main()
{
var c = (Cartesian)new Polar(1.0, 0.0);
Console.WriteLine(c);
}
}変換は演算子と組み合わせて使える
変換が定義されると、その値は変換先の型の式に参加できるようになります。double への暗黙的変換を定義すると、自分の型をそのまま算術演算で使えます。
using System;
public struct Meters
{
public double Value;
public Meters(double v) { Value = v; }
public static implicit operator double(Meters m) => m.Value;
}
public class Program
{
public static void Main()
{
var distance = new Meters(5);
double doubled = distance * 2; // uses implicit conversion to double
Console.WriteLine(doubled);
}
}予期しない暗黙的変換を避ける
明らかに安全とはいえない暗黙的変換は、コンパイラーが黙って適用するため、バグを隠してしまうことがあります。迷った場合は、意図がコード上で明確になるよう、明示的変換を選んでください。
using System;
public struct UserId
{
public int Value;
public UserId(int v) { Value = v; }
// Explicit on purpose: an int is not always a valid id
public static explicit operator UserId(int v) => new UserId(v);
public static explicit operator int(UserId id) => id.Value;
}
public class Program
{
public static void Main()
{
var id = (UserId)42;
int raw = (int)id;
Console.WriteLine(raw);
}
}変換演算子は static
他の演算子と同様に、変換演算子は public static です。関係する型のうち、演算子を宣言する型は必ず1つだけでなければなりません。
using System;
public struct Fahrenheit
{
public double Degrees;
public Fahrenheit(double d) { Degrees = d; }
public static implicit operator Fahrenheit(double d) => new Fahrenheit(d);
public static implicit operator double(Fahrenheit f) => f.Degrees;
public override string ToString() => Degrees + "F";
}
public class Program
{
public static void Main()
{
Fahrenheit f = 98.6;
double d = f;
Console.WriteLine(f + " = " + d);
}
}実用的なラッパー型
変換は、型システムを強化しながら使い勝手も損なわない薄いラッパー型で特に効果を発揮します。ここでは、NonEmptyString から string へは暗黙的に変換できますが、作成時には明示的なキャストが必要です。
using System;
public struct NonEmptyString
{
public string Value;
public NonEmptyString(string v)
{
if (string.IsNullOrEmpty(v)) throw new ArgumentException("empty");
Value = v;
}
public static explicit operator NonEmptyString(string s) => new NonEmptyString(s);
public static implicit operator string(NonEmptyString s) => s.Value;
}
public class Program
{
public static void Main()
{
var name = (NonEmptyString)"Ada";
string s = name;
Console.WriteLine(s);
}
}自分で試してみる
安全な暗黙的変換で外側の型へ変換でき、検証を伴う明示的変換で内側の型へ変換するラッパーを定義してください。どちらの方向でキャストが必要になるか確認しましょう。
using System;
public struct Ratio
{
public double Value;
public Ratio(double v)
{
if (v < 0 || v > 1) throw new ArgumentOutOfRangeException(nameof(v));
Value = v;
}
public static implicit operator double(Ratio r) => r.Value; // always safe
public static explicit operator Ratio(double v) => new Ratio(v); // may throw
}
public class Program
{
public static void Main()
{
var half = (Ratio)0.5; // explicit, validated
double d = half; // implicit, safe
Console.WriteLine(d * 100 + "%");
}
}クイックチェック
適切な変換の種類を選んでください。
まとめ
ユーザー定義変換を使うと、自分の型同士を自然に連携させられます。
- 常に安全でデータが失われない変換には
implicit operatorを使います。 - データが失われたり、変換に失敗したりする可能性がある場合は
explicit operatorを使います。 - 変換先は組み込み型でもカスタム型でもよく、どちらの方向にも変換できます。
- 変換演算子は
public staticで、関係する型のいずれかによって宣言されます。
よくある質問
「ユーザー定義変換」レッスンは無料ですか?
はい。「ユーザー定義変換」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C# Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C# Academyコースには全4レッスンが含まれています。
「ユーザー定義変換」で何を学びますか?
暗黙的変換演算子と明示的変換演算子を追加します。 ブラウザで直接実行するハンズオンコードでC# Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
C# Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのC# Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「ユーザー定義変換」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このC# Academyレッスンでコードを書いて実行できますか?
はい。すべてのC# Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。