前言:本博文记录书中新学到的知识点,值得思考知识点以及一些难点。
第四章 类型基础
4.1所有类型都从System.Object派生
Object.ToString():
默认返回 = this.GetType().FullName
源码标注:
印证:
static void Main(string[] args)
{
object obj = new object();
Console.WriteLine("ToString:" + obj.ToString() + ",FullName:" +
obj.GetType().FullName);
Console.ReadLine();
}
结果:
平时输出对象的时候其实就是输出的ToString,对于C#来说,这是VS的功劳。
new与override的不同:
public class TestNew
{
public string Tea()
{
return "茶";
}
}
public class TestNew1 : TestNew
{
public new string Tea()
{
return "Tea";
}
}
static void Main(string[] args)
{
TestNew test = new TestNew1();
Console.WriteLine(test.Tea());
Console.ReadLine();
}
输出:
而使用vitrual和override会输出Tea.
第五章 基元类型、引用类型和值类型
5.1 编程语言的基元类型
类似于Int32、Int64,通过源码可知:
32位与64位的int不存在子父类关系,但32位却可以向64位做转换,这得益于编译器的功劳,编译器会自动识别此类转换关系做隐式转换。
对于会产生数据丢失的类型转换,例如Double到Int32,C#总是做向下取整的处理,如(Double)7.9 => (Int32)7.
checked和unchecked基元类型操作
在vs中打开编译器检查溢出:项目->属性->生成->高级->检查运算上溢/下溢。
- 使用unchecked操作符,不会检查代码溢出。
Int32 k = int.MaxValue; Int32 l = unchecked(k + 1); Console.WriteLine(l);
结果:
- 使用checked操作,代码溢出会抛出OverflowException:
值类型的装箱与拆箱(尽可能的在自己的代码中减少装箱的次数)
看看下面的代码会出现几次装箱动作?
public static void Main(string[] args)
{
Int32 i = 1;
Object j = i;
i = 123;
Console.WriteLine(i + "," + (Int32)j);
}
答案是:3次。开始怎么都没想明白,最后通过书中解释恍然大悟。
首先,Object j = i;很明显,i被装箱一次。
Console.WriteLine(i + "," + (Int32)j),我们来看看它的IL代码:
注意在WriteLine中使用+号编译器会隐式的处理未String.Concat方法,通过编译之后的IL源码也可以看到,有调用Concat的方法,但这里注意我们做的是整型的连接,所以会调用:
所以i会被装箱成arg0,这是第二次装箱。(Int32)j会先拆箱然后装箱,这是第三次装箱。
而装箱操作会影响应用程序性能和增加内存消耗(装箱会在托管堆新建对象),所以提升的做法是降低装箱率,下面是对上述代码的优化:
public static void Main(string[] args)
{
Int32 i = 1;
Object j = i;
i = 123;
Console.WriteLine(i.ToString() + "," + j);
}
这样之后,便只有Object j = i这一行会装箱了。
还有几种不易被发现的装箱行为:
public static void Main(string[] args)
{
Int32 i = 1;
i.GetType();//调用基类方法
IComparable j = i;//i转型为接口类型
}
IL: