用户定义的转换
添加隐式和显式转换运算符。
用户定义的转换 是 CoddyKit 上的免费 C# Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 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);
}
}自定义类型之间的转换
转换并不局限于内置类型。您可以在两个自定义类型之间进行转换,例如将极坐标转换为笛卡尔坐标。
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。参与转换的两个类型中必须恰好有一个是声明该运算符的类型。
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,并由参与转换的某个类型声明。
常见问题解答
「用户定义的转换」课时是免费的吗?
是的 — 「用户定义的转换」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C# Academy 课程的其余内容,请升级到 CoddyKit PRO。 C# Academy 课程共包含 4 节课。
「用户定义的转换」这节课中我会学到什么?
添加隐式和显式转换运算符。 你通过在浏览器中直接运行的动手代码来练习 C# Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 C# Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 C# Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「用户定义的转换」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 C# Academy 课中编写并运行代码吗?
能。每节 C# Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。