第五章《基元类型、引用类型、值类型》
- 编程语言的基元类型
- 引用类型和值类型
- 值类型的装箱拆箱
- 对象哈希吗
- dynamic基元类型
5.1 编程语言的基元类型
编译器直接支持的数据类型,在这里称之为基元类型,因为CLR
对基元类型足够的了解,所以在执行对基元类型的对象的操作的时候,会比其余类型更加的迅速。
在上表中,基元类型都是有对象FCL类型的,所以我们纠结string
和String
的时候就没有道理了,string
会自动映射成FCL类型的String
,所以两者是一样一样的
5.1.1checked
和unchecked
对基元类型的操纵是很容易造成数据溢出的。下面是一个简单的checked
检查块
checked
{
byte b = 100;
byte b = (byte)(b+200);//程序会进行溢出检查
}
5.2 引用类型和值类型
在潜意识里,引用类型和值类型的最大的区别在于一个是在堆上分配,一个是在栈上分配,这样在传递值的时候,一个是复制指针(一变都变),一个是复制值(后变前不变)。
其实,在多数的公司里,是很少用到值类型(enum,strut
)的,因为公司根本不在乎这一丁点的性能损耗,所以不再过多的赘述。
5.3 值类型的装箱和拆箱
装箱:
很多时候我们需要对值类型添加引用,比如做的第一个项目的返回页面total
值,显然要时时获取最新的total
值,必然需要对值类型total进行装箱操作。一旦进行装箱操作,就会给值类型分配堆地址,这样一来,值类型就成了引用类型
拆箱:
没什么好说的,引用类型转成值类型就完成了拆箱操作如
Int32 v = 5;
object o = v;
v = 123;
Console.WriteLine(v + "," +(Int32) O)//一共进行了三次装箱操作一次拆箱操作,如果感到意外,重新翻一遍本章书籍
5.4 对象哈希码
对于Equals
方法和GetHashCode
方法,考虑在项目中运用的过少,这里也不在研究,在后续如果有机会进行补充。
5.4 dynamic
基元类型
同上
第六章《类型和成员基础》
- 类型的各种成员
- 类型的可见性
- 成员的可访问性
- 静态类
- 分布类、结构和接口
- 组件、多态和版本控制
6.1 类型的各种成员
类的成员有:
- 常量:数值恒定不变的符号
- 字段:只读或者只读/可写的数据值
- 实例构造器:土话说的构造函数,给新对象一个良好的初始化的方法
- 类型构造器:将静态对象一个良好的初始化的方法
- 方法:更改或查询数据类型或对象状态的函数
- 属性:
- 事件:
- 类型:就是类,非要整个类型类型,看的我累死!
在初学C#的时候,一定会有疑惑,为什么一个程序可以容纳不同的编程语言呢,这里就能体现元数据的强大了,这是因为编译器会将源代码统一编译成CLR能识别的元数据,而元数据的格式又是完全一致的,这样就能无缝链接另一个编程语言的代码了。
6.2 类型的可见性
- internal :当前程序集可见
- public :所有程序集可见
友元程序集:没用到过,大致就是在引用一个命名空间之后,用它里面的一个方法,然后添加公钥,这样在该类中即使不讲类型指定为public,在别的程序集中同样可以使用该类的所有internal类和成员
6.3 成员的可访问性
CLR术语 | C# | 描述 |
---|---|---|
Private | private | 只能有当前类或者嵌套类使用 |
Family | protected | 只能由当前类或者当前程序集的派生类使用 |
Assembly | internal | 当前程序集可以使用 |
Public | public | 任何程序集都可以使用 |
在C#中,类的成员默认是private的,而接口的成员默认是public的。
派生类型重写基类类型的时候需要保证,重写成员和基类成员的访问限制要一致。
6.4 静态类
这是一个不需要实例化的特殊类,是直接从System.Object派生的类,因为它不需要实例化,因此该类就有很多因为不能实例化而产生的很多不一样的地方:
- 静态类是必须被static加以限制的,这毋庸置疑
- 静态类的成员,方法,事件等等都必须被必须是static的
- 是没有办法调用接口的
6.5 分布类、结构、接口
很少有使用到partial关键字,所以这里不做解释
6.6 组件、多台和版本控制
C#关键字 | 方法\属性\事件 |
---|---|
abstract | 类里的成员必须被重写 |
virtual | 可以被重写的定义关键字 |
override | 确定来重写的派生类的关键字 |
sealed | 规定不能被重写的关键字 |
归论正章,建议今后在定义已经明确了不会被重写的类都加上sealed关键字,调用虚方法在性能上是不及非虚方法的,虚方法需要默认去查找类对象的类型
有个很关键的关键字区别,new是替代原有的方法,之后调用方法的时候,都会调用被new关键字修饰过的方法,而override可以理解成重写,可以在方法里面添加或者要删除的代码,并且可以选择用base来调用基类原始的方法。
第七章《常量和字段》
- 常量
- 字段
7.1 常量
const
:常量是值从不变化的符号,一旦定义将直接产生元数据,嵌入到IL代码中,正式因为会直接嵌入到IL代码中,所以一开始就要给const
定义初始值,在给非基元类型定义初始值的时候,我们应该给它定义一个null
值。
7.2 字段
有一个好玩的例子
public static class AType
{
public static readonly string[] InvalidChars = new string[] { "A", "B", "C" };
}
public sealed class AnotherType
{
public static void Main()
{
AType.InvalidChars[0] = "X";
AType.InvalidChars[1] = "Y";
AType.InvalidChars[2] = "Z";
}
}
发现了嘛,上述的数组字段设的是readonly
,但是却可以给该数字的对象值设值,是不是很神奇?这是因为数组是引用类型,不可以改变或者说只读的是它的引用,而不是对象,所以我们可以给它的对象设值,但是不可以给它的引用设值。现在来试一下改变它的引用来给其设值
AType.InvalidChars = new string[]{"X","Y","Z"}
第八章 《方法》
- 实例构造器和类(引用类型)
- 实例构造器和结构(值类型)
- 类型构造器
- 操作符重载方法
- 转换操作符方法
- 拓展方法
- 分布方法
8.1 实例构造器:构造方法(构造函数)
接下来我们还是用土名字构造方法来解释它吧:构造方法是给类里的对象初始化状态用的,如果没有显示定义呢,它会自动生成一个无参的构造函数,构造函数会默认将所有的字段都设为默认值,然后依次给其赋值为你所定义的值。就是酱紫!
用一个简单的例子来说明C#中利用this关键字来调用另一个构造器:
internal sealed class SomeType
{
private Int32 m_x;
private String m_s;
private Double m_d;
private Byte m_b;
public SomeType()
{
m_x = 5;
m_s = "damon";
m_d = 3.16;
m_b - 0xff;
}
//这个构造器呢,会首先将所有的字段全部初始化为0或null,然后才对其赋值
public SomeType(Int32 x):this()
{
m_x = x;
}
}
8.2 实例构造器和结构(值类型)
略
8.3 类型构造器
略
8.4 操作符重载方法
略
8.5 扩展方法
前提条件:
- 静态类中的静态方法,在静态方法的第一个参数前添加this关键字,达到重写和扩张该方法的目的,在CLR中,会默认给扩展方法前添加属于扩展方法的特性,以便于CLR去匹配和查找所需要的扩展方法
- 扩展方法扩展自哪个类型,就必须用该类型的变量来使用如:
public static string DateToString(this DateTime dt)
{
return dt.ToString("yyyy-mm-dd hh:mm:ss");
}
static void Main(string[] args)
{
DateTime now = DateTime.Now;
string time = now.DateToString();//这里的now就是方法里的第一个参数类型
Console.WriteLine(time);
Console.ReadKey();
}
8.6分布方法
大多使用的地方都是在代码生成器里滴定义的,所以这里略过了。
第九章《参数》
- 可选参数和命名参数
- 隐式类型的局部变量
- 以传引用的方式向方法传递参数
- 向方法传递可变数量的参数
- 参数和返回类型的设计规范
- 常量型
9.1可选参数和命名参数
可选参数:这个概念很简单,在方法的参数里给参数一些默认值,当调用方法的时候,实参的参数列表里如果没有,就会用默认的参数
命名参数:在调用方法的时候,给实参用:关键字来为其特定的赋值
private static void UpdateLatestDate(int x = 9,string y = "damon")
{
Console.Writeline(x+y);//3damon
}
UpdateLateDate(x:3)
9.2 隐式类型的局部变量-----var
var
用在不能清楚的获得变量类型和类型名字过长的时候。有四个显要特点:
- 必须初始化,即
var s = "damon" ;//var s; s = damon;不被允许;
2.必须声明的是局部变量
3.一旦初始化完,就不能够在更改类型
4.使用var定义变量和object
不同
不足之处在于:过多的使用var
会让代码显得不够明了,毕竟代码是给人看的,而不是给编译器看的!
9.3以传引用的方式向方法传递参数
在声明参数或是调用参数的时候,任何一方加上了ref
或out
关键字,都必须同时显示声明出该关键字。
ref
:用前必须初始化,可读可写
out
:用前不用初始化,但是一定要调用的时候给这个值写入
9.4向方法传递可变数量的参数----params
public class Test2
{
public static void Main()
{
ShowName("小兵");
ShowName("小王", "小六"); //在对参数个数不明的情况下使用
}
public static void ShowName(params string[] names)
{
foreach (string name in names)
{
Console.WriteLine(name);
}
}
}
限制:
params
参数修饰的必须是一维数组- 该一维数组类型可以是
Object
params
修饰的必须是最后一个参数
9.5参数和返回类型的设计规范
- 方法参数用弱参数类型,也就是大的笼统的参数类型,这样确保调用方法到时候有更大的灵活性,方法适用的范围更大
- 方法返回用强返回类型,这样限制方法执行后的结果更统一
第十章《属性》
- 无参属性
- 有参属性
- 调用属性访问器方法时的性能
- 属性访问器的可访问行
- 泛型属性访问器方法
10.1无参属性
就着重讲解一下自动实现的属性(AIP)吧:
只要定义了AIP,那它一定是已经chuan创建了属性,访问该属性就会默认调用它的get,Set方法。
public string VendorNumber { get; set; }
在本质上,属性可以理解成方法!属性更像是对字段的封装,拥有get,set的索引器方法。之所以称之为无参属性,那是因为get方法不接受任何的参数。
对象初始化器:
不需要再绞尽脑汁去想创建多少个构造函数才能合理达到初始化多个不同类型和个数的对象了
public class Book
{
/// <summary>
/// 图书名称
/// </summary>
public string Title { get; set; }
/// <summary>
/// 单价
/// </summary>
public float Price { get; set; }
/// <summary>
/// 作者
/// </summary>
public string Author { get; set; }
/// <summary>
/// ISBN号
/// </summary>
public string ISBN { get; set; }
}
//对象初始化器
Book book = new Book { Title="Inside COM",ISBN="123-456-789"};
其实上面的那一长串依然是声明一个对象而已,所以可以接着使用它
Book book = new Book { Title="Inside COM",ISBN="123-456-789"}.ToString.ToUper;
再来一个集合初始化器
IList<Book> books = new List<Book> {
new Book { Title = "Inside COM", ISBN = "123-456-789",Price=20 },
new Book { Title = "Inside C#", ISBN = "123-356-d89",Price=100 },
new Book { Title = "Linq", ISBN = "123-d56-d89", Price = 120 }
};
匿名类型:举报现在都是匿名的,因为啥?因为举报别人找不着报复对象呗。
var KeyPair = new {Key=”yuyi”,Value=”20”};
10.2 有参属性
用的太少,略;