首页 > 编程笔记 > C#笔记

C#类型转换和GetType方法

在运行时,可以通过 GetType 方法获得对象指向的类型对象的类型。

当需要类型转换时,有如下的几种情况:

假设我们有如下的两个类:
public class A
{
    public int a { get; set; }
}
publie class B : A
{
    public int b { get; set; }
}

将一个对象转换为它的基类型

这种情况永远都能转换成功,所以 C# 不要求额外的语法。当然,也可以使用显式转换:
A a = new B();
Console.WriteLine(a.GetType() ) ; // B
A a2 = (A)new B();
Console.WriteLine (a2 . GetType () ) ; // B
此时,我们看到 GetType 方法返回的值是“当前命名空间名 .B”。

但是如果我们试图在 Visual Studio 中访问对象 a/a2 的成员,会发现它只有 a —个成员,并没有 b (实际上,可以编写 IL 访问 B 的方法)。那么 a/a2 的类型究竟是什么呢?(答案是A)

实际上,根据之前对象初始化的知识,我们已经知道,在使用 new 关键字时,会在堆上初始化类型对象和普通对象。

当我们运行完上面的四行代码之后,内存中的布局大家应该可以想象出来了,如下图所示。

运行完上面四行代码后的内存布局

当调用 GetType 方法时,实际上获取的对象是指向的类型对象的名称 B,但是,这不意味着对象本身就是这个类型。

对象本身的类型是在定义时就决定的,在本例中为 A 类型(编译时类型)。至于 new 后跟的类型 B,只是意味着,对象(本例中是一个引用类型)在栈上所保存的引用指向一个类型 B 的实例(运行时类型)。

new 关键字会完成 B 的初始化工作, 然后返回堆上对应的地址。

在基类引用中保存派生类型总是安全的,这是隐式转换。

运行时类型决定对象调用方法时去哪个方法表,而编译时类型是 Callvirt 决定最终调用哪个类型的哪个方法。

结论:

将一个对象转换为它的派生类型

对于这种情况,C# 要求必须使用显式转换,因为这样的转换可能会在运行时失败。
// B b = new A(); 不能通过编译
B b = (B)new A();

基元类型的类型转换

这是一个特例,对于逻辑上有从属关系的基元类型,C# 可以完成转换。

例如,你可以隐式地将一个 int 转换为 long,即使这两个结构体没有继承关系。

这是因为,C# 会在基元类 型的类型转换时使用自己的特殊规则,而 int 和 long 逻辑上有从属关系。

从较大的集合转换为较小的集合时,C# 总是要求显式转换。例如,long 转换为 int 必须显式转换,而反过来则可以隐式转换。

自定义类型转换

对于拥有继承关系的两个类,我们可以做显式转换和隐式转换,这取决于类的包含关系。

但如果两个类或者结构并没有继承关系,我们仍然可以通过自定义类型转换实现类的类型转换。

自定义类型转换需要 explicit 和 implicit 关键字。下面的例子实现了分数,它包括分子和分母两个成员,并实现了加和乘的重载。
class Fraction
{
    public int fenzi { get; set; }
    public int fenmu { get; set; }
    public Fraction(int X,int Y)
    {
        fenzi = X;
        fenmu = Y;
    }
    //两个分数相加
    public static Fraction operator +(Fraction p1,Fraction p2)
    {
        return new Fraction(p1.fenzi * p2.fenmu + p2.fenzi * p1.fenmu, p1.fenmu + p2.fenmu);
    }
    //两个分数相乘
    public static Fraction operator*(Fraction p1,Fraction p2)
    {
        return new Fraction(p1.fenzi * p2.fenzi, p1.fenmu * p2.fenmu);
    }
    public static explicit operator double(Fraction f1)
    {
        return (double)f1.fenzi / f1.fenmu;
    }
}
之后我们就可以显式地将分数转化为小数:
Fraction f1 = new Fraction(1, 4);
Console.WriteLine((double) f1); // 0.25

注意:当有自定义类型转换时,构造函数必须是公有的,关键字 public 不能省略。

自定义类型转换的实质是重载类型参数的行为。如果为一个 a 类 -> b 类构建了显式转换,则不能同时构建隐式转换(隐式转换不会成功)。

不过如果定义了隐式转换,则显式转换自动 生效(两种转换都会成功)。

所有教程

优秀文章