一、C#是强类型的语言,即每个对象有且只有一种类型。这个类型在对象创建时就已经完成定义,并在对象执行期间保持不变,C#的所有变量在使用之前都必须完成初始化。
二、托管线程栈
1) 每个windows线程都有一个私有的内存区域,称为栈。线程的栈的作用在于
i. 保存正在执行中的方法的传入实参值;
ii. 保存方法返回时需要跳转的本地代码的地址;
iii. 保存对象
2) 栈的大小是可变的,通常以1MB为上限
三、托管堆
一个进程只有一个堆,这是在进程地址空间中的一个内存区域。所有统一进程中的线程都可以访问这个区域。就是说,堆与栈不同,对不专属于某个线程。堆的主要作用是存储对象。而且堆的大小象栈一样,在执行期间也是可以变化的,但是,堆大小的上限比栈的上限大得多。
四、对象存储|:托管线程栈与托管堆的比较
1) 堆的优点是容量比栈大得多
2) 栈的优点是存取速度比堆要快,这主要源于专门访问栈的IL指令。还有一个原因是访问展上的元素无需同步。
3) 因此,.NET设计成用堆来存储大对象而用栈来存储小对象
五、静态分配与动态分配的比较
1) C++与C#有一个一致的地方,那就是对象要么分配在线程栈上(我们称为静态分配),要么分配在进程堆上(我们称为动态分配)
2) C++与C#不同之处在于分配模式的选择的方式
i. C++中对象的分配模式是由程序员自动选择的。代码中直接声明的对象将使用静态分配,如(int i=0)。当对象是使用new操作符时候采用动态分配(比特 int * pi = new int (0)
ii. C#中对象的分配模式取决于对象的实现方式。值类型的实例采用静态分配,而引用类型的实例则使用动态分配
iii. C#程序员的责任比C++程序员来说显出降低了
- 不需要选择对象的分配方式
- 不需要担心对象解除分配的问题。
六、引用类型与值类型
1) .NET中每一种类型要么是值类型,要么是引用类型。值类型的实例通常是在线程栈上分配的,但是在某些情形下可以存储在堆中。引用类型的对象总是在进程堆中分配(动态分配)
七、公共类型系统CTS
1) 基本类型:指整数、浮点数、字符 和布尔等类型
2) 枚举
3) 结构
4) 类:都是引用类型。特别注意表示字符串的system.string类型和表示数组的system.array类型
5) 委托类型。它的实例可以应用一个方法,作用如同C++的函数指针
6) 指针。
八、对象比较
1) objectA.equals(objectB)
2) object.ReferenceEquals(objectA,objectB)
九、对象复制
1) 如果是值类型的实例,那么“=”复制运算符及iukeyi将源对象的状态逐字节地复制到目标对象中。而如果是引用类型的实例,“=”赋值运算符仅复制引用,而不是对象本身。因而引用类型需要一种复制引用类型的对象状态的方法。System.IConeable接口就是专门为这项工作准备的
2) 浅复制memberwiseclone()方法
class Article {
public string Description;
public int Price;
}
class Order : System.ICloneable {
public int Quantity;
public Article Article;
public override string ToString() {
return "Order: " + Quantity + " x " + Article.Description +
" Total cost: " + Article.Price * Quantity;
}
public object Clone() {
// 浅复制
return this.MemberwiseClone();
}
}
class Program {
static void
Order order = new Order();
order.Quantity = 2;
order.Article = new Article();
order.Article.Description = "Shoes";
order.Article.Price = 80;
System.Console.WriteLine(order);
Order orderClone = order.Clone() as Order;
orderClone.Article.Description = "Shirt";
System.Console.WriteLine(order);
}
}
以上例子的结果是
order:2*shoes total cost:160;
order:2*shirt total cost:160;
可以看到,对克隆订单的商品的修改反映到了原始订单的商品上。这是因为,这个程序中只有一个Article类的实例,两个order类都引用了这个唯一的实例。
3) 深复制
class Article : System.ICloneable {
public string Description;
public int Price;
public object Clone() {
// 此处,浅复制等于深复制
return this.MemberwiseClone();
}
}
class Order : System.ICloneable {
public int Quantity;
public Article Article;
public override string ToString() {
return "Order: " + Quantity + " x " + Article.Description +
" Total cost: " + Article.Price * Quantity;
}
public object Clone() {
// 深复制
Order clone = new Order();
clone.Quantity = this.Quantity;
clone.Article = this.Article.Clone() as Article;
return clone;
}
}
class Program {
static void
Order order = new Order();
order.Quantity = 2;
order.Article = new Article();
order.Article.Description = "Shoes";
order.Article.Price = 80;
System.Console.WriteLine(order);
Order orderClone = order.Clone() as Order;
orderClone.Article.Description = "Shirt";
System.Console.WriteLine(order);
}
}
以上例子的结果是
order:2*shoes total cost:160;
order:2*shoes total cost:160;
这个例子中article类的浅复制与深复制是等价的,我们可以断定,针对一个给定的类,他的浅复制与深复制等价当且仅当所有成员均为值类型。不过article类中有一个字段是string类型,而他是引用类型,但string类有特殊性质,其中之一是它的实例都是不可变得。这个特性使得字符串在许多场合都和值类型的实力非常相似
4) 复制构造函数:这种构造函数接受一个我们想要使用的副本的类型作为参数
class Article : System.ICloneable {
public string Description;
public int Price;
public object Clone() {
return this.MemberwiseClone();
}
}
class Order {
public int Quantity;
public Article Article;
public override string ToString() {
return "Order: " + Quantity + " x " + Article.Description +
" Total cost: " + Article.Price * Quantity;
}
// Default constructor.
public Order() { }
// 复制构造函数
public Order( Order original , bool bDeepCopy) {
this.Quantity = original.Quantity;
if( bDeepCopy )
this.Article = original.Article.Clone() as Article;
else
this.Article = original.Article;
}
}
class Program {
static void
Order order = new Order();
order.Quantity = 2;
order.Article = new Article();
order.Article.Description = "Shoes";
order.Article.Price = 80;
System.Console.WriteLine(order);
Order orderClone = new Order( order , true );
orderClone.Article.Description = "Shirt";
System.Console.WriteLine(order);
}
}
十、 装箱与拆箱
1) 作为方法局部变量的值类型的实例是直接存储在线程栈上的。在本线程中使用这些值类型实例无需通过指针或引用。有些方法所需参数是引用类型object类,所有值类型都从object派生,但是值类型的实例没有引用,这个过程即装箱
class Program {
static void f( object o ) { }
public static void
int i = 9 ;
f( i );
}
}
以上实例运行并不出错,正是装箱让我们获取了一个本来没有引用的值类型实例的引用,装操作分三步完成:
- 该值类型创建了一个新实例,并分配在堆中
- 这个堆中的实例根据栈中实例的状态进行初始化。在上面例子中,我们的证书进行了一次四字节的复制,可以说初始的对象实例被克隆了一份
- 用这个指向新创建的实例引用取代了原先在栈中分配的实例。
2) 相反的过程即拆箱
class Program {
public static void
int i = 9;
object o = i; // i is boxed
int j = (int) o; // o is unboxed
}
}
3) 在C#中装箱和拆箱是隐式执行的。
十一、 基本类型
1) C#没有unsigned关键字,现在用byte、ushort、uint、和ulong来表示无符号整数
在c#中long和ulong的大小是8字节
C#中decimal 16字节可以表示多达28位的有效数字的精确实数
C#中short是2字节
C#中int是32位
2) 关于代码的小问题
Int i=1000000000//i=10亿
Long j=10*I;
结果j不是100亿。实际上,这个计算是基于int类型计算的(10是int型),所以结果被复制到了一个long变量中。这里最大的问题是开发人员不会收到任何警告信息,除非使用了checked关键字。解决这个问题的方法是通知编译器希望使用8字节的整形,方法是将L加到字面常量后面
using System;
class Program {
public static void
int i = 1000000000;
long j =
Console.WriteLine(j);
}
}
十二、 基本类型的运算
1) 5种基本算术运算符+-*/%
2) 5中基本运算符对应的赋值运算符
I+=j;i=i+j;
i-=j;i=i-j;
i*=j;i=i*j
i/=j;i=i/j;
i%=j;i=i%j;
3) 运算符的优先级
i. +-×/的优先级高于%
ii. 可以用圆括号提升运算符的优先级
iii. 两种不同的基本类型变量之间进行算术运算时,结果的类型将为两个类型中取值范围较大的一个。
iv. 任何整形和浮点型之间都允许强制类型转转换。可以用checked关键字。
4) 位运算
i. <<左移位 相当于乘2
ii. >>右移位相当于除2
iii.
十三、 结构
1) 结构是值类型,实例存储在栈中,因此结构不适宜太大。太大的结构最好用类代替;
2) 结构不能继承自其它的类或结构,也不能作为其他派生类或结构的基类;
3) 与类的字段不同,结构的字段不能在声明中显示初始化;
十四、 枚举和整形
1) 编译器默认将枚举的值设为int型整数;
2) Object.Tostring()已经自动为每个枚举进行了覆写,作用是返回枚举常数定义时的名称的字符串;
3) System.Enum类
String[] GetNames(Type type),返回一个枚举类型中所有值的名称的字符串数组
class Program {
enum Maker { Renault, Ford,
static void
foreach( string s in System.Enum.GetNames( typeof(Maker) ) )
System.Console.WriteLine(s);
}
}
十五、 字符串
1) 转义字符:\
2) 双引号\”,反斜杠\\,空字符\0,回车符\r,水平制表符\t,单引号\’
3) 无转义字符串常量:@ 将接受字符串常量中所有的换行。这特性在生成代码的时候非常有用
4)
十六、 System.Text.StringBuilder类
1) Stringbuilder append()在字符串的结尾增加字符
2) Stringbuilder insert (int index,)将字符插入到index指定的位置。位置为0表示插入到字符串头部
3) Stringbuilder remove(int startindex,int length)将字符串从startindex到startindex+length位置的字符串删除。如果这两个位置之一小于0或者字符串的长度的长度就会引发argumentoutofrangeexception异常
class Program{
static void Display(System.Text.StringBuilder s) {
System.Console.WriteLine("The string : \"{0}\"",s);
System.Console.WriteLine(" Length : {0}", s.Length);
System.Console.WriteLine(" Capacity : {0}", s.Capacity);
}
public static void
System.Text.StringBuilder s = new System.Text.StringBuilder("hello");
Display(s);
//The string : "hello"
// Length : 5
// Capacity : 16
s.Insert( 4 , "--salut--" );
Display(s);
//The string : "hell--salut--o"
// Length : 14
// Capacity : 16
s.Capacity = 18;
Display(s);
//The string : "hell--salut--o"
// Length : 14
// Capacity : 18
s.Replace("salut","HELLO EVERYBODY");
Display(s);
//The string : "hell鈥擧ELLO EVERYBODY--o"
// Length : 24
// Capacity : 36
s.EnsureCapacity(42);
Display(s);
//The string : "hell--HELLO EVERYBODY--o"
// Length : 24
// Capacity : 72
}
}
十七、 委托类和委托对象
1) C#允许用delegate关键字创建一种特殊的类,我们称之为委托类,委托类的实例称为委托对象。
从概念上说,委托是一种指向一个或多个方法(静态或非静态)的引用。我们可以用调用方法的语法调用委托对象,这样会调用委托对象所引用的方法。
2) 委托类推测功能
public class Program {
delegate void Deleg1();
delegate string Deleg2( string s );
static void f1() {
System.Console.WriteLine("f1() called.");
}
static string f2(string s) {
string _s=string.Format( "f2() called with the param \"{0}\"." , s );
System.Console.WriteLine( _s );
return _s;
}
public static void
//查看IL语法,实际上还是调用了deleg1和deleg2委托类的构造函数
Deleg1 d1 = f1; // 代替Deleg1 d1 = new Deleg1( f1 );
d1();
Deleg2 d2 = f2; // 代替 Deleg2 d2 = new Deleg2( f2 );
string s = d2("hello");
}
}
3) 委托对象和实例方法
委托可以以同样的方法引用实例方法。
using System;
public class Article {
private int m_Price = 0;
public Article( int price ) { m_Price = price; }
public int IncPrice( int i ) {
m_Price += i;
return m_Price;
}
}
public class Program {
public delegate int Deleg( int i );
public static void
// Create an article with a price of 100.
Article article = new Article( 100 );
// Create a delegate object that references the 鈥業ncPrice()鈥?
// method on the object 鈥榓rticle鈥?
Deleg deleg = article.IncPrice;
int p1 = deleg( 20 );
Console.WriteLine("Price of article: {0}", p1 );
int p2 = deleg( -10 );
Console.WriteLine("Price of article: {0}", p2 );
}
}
4) System.Delegate类
实际上,当一个委托对象引用多个方法时,每个方法都需要创建一个System.Delegate类的实例,实际上MulticastDelegate类的实例可以看成是一个System.Delegate类的实例的列表。
以下的代码好好学习
using System;
public class Article {
public int m_Price = 0;
public Article( int price ) { m_Price = price; }
public int IncPrice( int i ) {
m_Price += i;
return m_Price;
}
}
public class Program {
public delegate int Deleg(int i);
public static void
Article a = new Article( 100 );
Article b = new Article( 103 );
Article c = new Article( 107 );
// deleg 指向 ( a.IncPrice , b.IncPrice , c.IncPrice ).
Deleg deleg = a.IncPrice;
Deleg deleg1 = b.IncPrice;
deleg1 += c.IncPrice;
deleg += deleg1;
deleg( 10 );
Console.WriteLine( "a:{0} b:{1} c:{2}",
a.m_Price , b.m_Price , c.m_Price );
//尝试移除子表 ( a.IncPrice , c.IncPrice )
// 并不在委托对象deleg中
Deleg deleg2 = a.IncPrice;
deleg2 += c.IncPrice;
deleg -= deleg2;
deleg(10);
Console.WriteLine( "a:{0} b:{1} c:{2}",
a.m_Price , b.m_Price , c.m_Price );
//尝试移除子表( a.IncPrice , b.IncPrice ) 包含在委托对象deleg中
Deleg deleg3 = a.IncPrice;
deleg3 += b.IncPrice;
deleg -= deleg3;
deleg(10);
Console.WriteLine( "a:{0} b:{1} c:{2}",
a.m_Price , b.m_Price , c.m_Price);
}
}