参数传递(ref、out、params):
1.当按引用方式(方法中对参数的更改将反映在该变量中)传递参数时,方法定义和调用方法都需显示地使用ref关键字。ref传递的参数变量须在传递前初始化
2.通过out关键字可以引用方法中的变量,方法定义和调用方法都需显示地使用out关键字
3.当要传入未知个数的同类型参数,可以使用params。如:void Average(params double[] x)
循环控制:
1.foreach迭代语句中不能对集合修改,要修改就要使用for遍历
2.do-while和while的区别是do-while至少执行一次执行体。
3.continue关键字作用是跳过循环体中剩余语句而执行下一次循环。对于while和do-while循环,continue是跳到条件判断表达式,对于for循环是跳到循环变量计算表达式(如i++)
数组:
1.数组定义和初始化:int[] 一维数组=new int[3]; int[] 一维数组={1,2,3}; int[] 一维数组=new int[3]{1,2,3};
int[,] 二维数组=new int[2,3];int[,] 二维数组={{1,2,3},{3,4,5}}
int[,,] 多维数组=...
int[][] 交错数组=new int[][]{new int[]{1,2},new int[]{3}};int[][] 交错数组=new int[2][]
访问修饰符:
1.publice:公共,在任何地方都可访问
2.private:私有,只能在本作用域中访问
3.protected:受保护,本类和派生类可访问
4.inernal:内部,本程序集中任何地方可访问
类型转换:
1.三种显式转换方式:a.“(数据类型)”形式--只支持兼容类型(比如都是数值类型)之间的强制转换
b.“Convert.To..”--支持的数据类型更广一些
c.“基础数据类型.Parse”--每种基本数据类型都带Parse方法,用于将字符串转成对应的数值类型
运算符重载和转换运算符(operator、implicit、explicit):
1.implicit用于隐式转换的实现,定义为:
public static implicit operator targetType(sourceType o)
explicit用于显示转换,定义为:
publice static explicit operator targetType(sourceType o)
2.这两种转换运算符主要用于自定义类与其他类型的转换,换句话说无法在内置类型之间重新定义类型转换,因此targetType和sourceType中至少有一个必须是用户定义的类型。另外,基类与其派生类之间也不能重新定义转换,因此object类不能被自定义转换
2 {
3 static void Main(string[] args)
4 {
5 long lngArea;
6 clsRectangle myclsRectangle = new clsRectangle(15, 10);
7 Console.WriteLine
8 ("以下将类对象myclsRectangle转换成为lngArea,此为隐式转换!\n");
9 //由于存在隐式转换的实现定义,将clsRectangle类对象指定给long类型变量时,
10 //自动转换为矩形面积值。如果矩形面积值超过long类型的允许值,则不能隐式转换
11 lngArea = myclsRectangle;
12
13 Console.WriteLine
14 ("lngArea的值等于myclsRectangle的面积:{0}\n",lngArea);
15 Console.WriteLine
16 ("以下将类对象myclsRectangle转换成为mySqure,此为强制转换!\n");
17
18 clsSquare mySquare = (clsSquare)myclsRectangle;
19 lngArea = mySquare.lngArea;
20 Console.WriteLine
21 ("lngArea的值等于mySquare的面积:{0}\n", lngArea);
22 Console.ReadLine();
23 }
24 class clsRectangle
25 {
26 int intWidth;
27 int intLength;
28 public int intArea;
29 public clsRectangle(int x, int y)
30 {
31 intWidth = x;
32 intLength = y;
33 intArea = intWidth * intLength;
34 }
35 public static implicit operator long(clsRectangle oclsRectangle)
36 {
37 long lngArea;
38 lngArea = oclsRectangle.intArea;
39 return lngArea;
40 }
41 public static explicit operator clsSquare(clsRectangle oclsRectangle)
42 {
43 int intWidth;
44 intWidth = oclsRectangle.intWidth;
45 clsSquare myclsSquare = new clsSquare(intWidth);
46 return myclsSquare;
47 }
48 }
49
50 class clsSquare
51 {
52 int intWidth;
53 public long lngArea;
54 public clsSquare (int x)
55 {
56 intWidth =x;
57 lngArea =x*x;
58 }
59 }
60 }
类、方法、重载(static、override、new、virtual、partial):
1.类默认的访问限定是internal,类里的成员默认访问限定是private。
2.静态类的成员必须都是静态的,而且不能有构造函数。普通类里可以有静态成员,为了阻止实例普通类,可以讲普通类的构造函数设置为private.
3.静态方法中不能访问非静态的类成员,非静态方法和非静态数据成员。但非静态成员方法中可以任意访问静态成员
4.方法重载必须是同一个类中的方法。满足名字相同,参数不同。当根据参数没有找到对应的方法时,c#将调用“最近”类型参数的方法。所谓“最近”规则即隐式转换,不能隐式转换时则报错
5.virtual 方法,除了表示可在子类中重写外,和普通方法完全一样。
abstract方法,只能出现在抽象类中,并且必须在子类重写。
extern方法,表示方法的代码体可以放在其他地方
6.在子类里用new隐藏了父类中的方法后,通过多态调用被重写的方法,可以执行父类里原来的方法。
在子类里用override重写父类中的方法后,通过多态调用被重写的方法,只能执行子类里重写的方法。
7.分部类允许将类代码放在多个文件中,所有部分都必须有相同的可访问性,如都是public、private。
构造方法:
1.构造方法允许是静态的。静态构造函数既无访问修饰符、无参数;在第一次 new 或第一次调用任何静态成员之前,将自动调用静态构造函数;静态构造函数一般用于初始化静态数据;不能直接调用静态构造函数。一旦父类里有静态构造函数,子类里就不可有无参的构造函数。无论创建几个包涵静态构造函数的类实例,静态构造函数只会被执行一次。
2.只要类中存在一个自定义的构造方法,系统将不再提供默认构造方法。此时如果不单独定义无参数的构造方法,将不能通过“new classname()"的方法创建对象实例。
3.可以通过base(父类构造函数对应的参数),来指定实例子类时调用的父类构造函数。也可通过this(当前类其他构造函数对应的参数),来指定实例子类时调用的自己的其他构造函数。
析构方法:
1.一个类只能有一个析构方法,析构方法定义规则和构造方法几乎相同,只是在构造方法前加“~”
2.无法继承或重载析构方法
3.无法自己调用析构方法,它们是被自动调用的
4.析构方法不用任何访问修饰符,也没有参数
属性:
1.属性不一定要和数据成员绑定
2.属性绑定的数据成员不受限制,但是最好从属性名能直接看出它是和哪个数据成员关联的
3.属性可以是静态的,使用起来和静态方法一样
4.get和set访问器可以有各自的访问修饰符,但一般都不用,默认是public类型
索引器:
public string this[int n]
{get{return names[n];}
set{names[n]=value;}
}
1.索引器是属性的一种,this关键字用于定义索引器
2.value关键字代表由set索引器获得的值
3.索引器不一定根据整数值索引,由你决定索引器参数的类型
4.索引器的[]里面可以是任意参数列表
5.索引器可被重载
继承(:):
1.protected的成员可在子类内部使用,但是不能被子类对象obj调用。只有public和internal的成员可以被对象在外部调用。在创建子类对象时,实际上父类的对象先被创建,然后是子类对象,内存中有两个对象。
2.通过base关键字可以指定调用的父类构造方法,缺省是base() 。base关键字还可用于引用父类的非私有成员。例如:base.fatherclassmethod()
抽象类(abstract):
1.抽象类不能直接使用new关键字创建实例。抽象方法不需要方法体,而抽象属性和索引器需要空方法体。
2.继承了抽象类的非抽象子类必须重写抽象类里的所有抽象方法。这是抽象基类的对象就可以引用子类实例的这个方法了。
p.Name="ealge"; //现在抽象类的对象可以引用子类里被重写的成员了
3.使用virtual关键字可以在普通类里创建虚方法(类似抽象方法,必须被override,不流行了)。抽象方法的修饰符不能是私有的,也不能是静态的。
接口(interface):
1.接口很像只包含抽象方法的抽象类。接口不能包含字段,可以包含方法、属性和索引器。成员不能有访问修饰符,默认访问修饰符是public。也不能用static。
2.继承了接口的类必须用public修饰符实现接口里的方法,但不需要使用override。
3.如果类继承了两个接口中有同名的方法,则需使用显示接口实现语句,而且不能为public了。
委托(delegate):[访问修饰符]delegate 返回值类型 委托类型名称(参数列表)
1.委托返回值类型和参数列表决定了委托能够代表的方法。
2.多播委托
3.多播委托的委托方法最好是没有返回值的,因为只有最后一个委托方法的返回值才能被返回。
4.多播委托只支持+、-、+=、-=四种运算。
5.匿名方法就是把方法体移到委托对象的赋值表达式右边。Say sayhello=delegate(){Console.Write("hello!");}
使用匿名方法,系统不必编译和创建单独的方法,因此效率较高。
事件(event):[访问修饰符]event委托类型 事件变量
1.
枚举(enum):
1.const和readonly都可用来定义常量。区别是readonly定义的常量可以在运行时再赋值,如:public readonly DateTime j = DateTime.Now;另外还可以在构造方法里修改。
2.c#中const只能修饰字段和局部变量,不能修饰方法。
3.枚举中的每个元素实际都是“符号/值”类型,默认第一个元素值是0,可以指定除了Char外的任何整型。
using system;
namespace MyNameSpace
{
enum Colors{Red=1,Green=2,Blue=4,Yellow=8};
class Program
{
public static void Main(string [] args)
{
//提取枚举成员中的数字,为“1”
int ncolor=(int)Colors .Red ;
//提取枚举成员的名字,为“Red”
string strcolor=Colors.Red.ToString();
//定义一个枚举对象
Colors cs=Colors.Red ;
//遍历枚举
foreach(string s in Enum .GetName (typeof (Colors )))
Console.WriteLine (s);
//将字符串转换成枚举对象
cs=(Colors )Enum.Parse(typeof(Colors),"Blue");
}
}
}
结构(struct):
1.结构可以视为轻量类,不能是抽象的,不能继承。
2.结构类型是值类型, 值类型定义后就会分配内存,而引用类型是动态分配内存。因此不宜定义复杂和大型的结构。不过值类型效率比较高。
3.编译器始终为结构提供一个默认的无参数构造方法,它是不能自定义的。
密封(sealed):
1.密封类除了不能作基类而被继承,也不能是抽象的,其他和普通类一样。
2.在子类里重写过的方法、字段、属性或事件,可以声明为密封成员,sealed关键字置于override前面。但以后的派生类里就不可以再次重写了。
可空类型(?):
1.可空类型的定义语法是"数据类型?",表示其基础值类型正常范围内的值再加上一个null值。主要用于数据库编程。
2.可空类型支持"??"运算符。"??"可以在将可空类型分配给非可空类型时返回的默认值。可空类型如果有值那么可以强行转换成对应值类型,或者也可用其Value属性直接赋值给同类值类型。使用可空类型的HasValue属性可以先判断是否有值
is和as运算符:
1.is检查某个对象是否与给定类型兼容,兼容是指是该类型或是派生于该类型的子类型。
2.as用于在兼容的引用类型之间执行类型转换。as与强制转换的区别是当转换失败时,运算符返回"null",而不是引发异常。
3.as等效于string str=os is string ?(string)os:null;
数学类(Math):
方法 |
说明 |
方法 |
说明 |
Abs |
求绝对值 |
Pow |
求x的y次幂 |
Max |
求较大的一个 |
Round |
求四舍五入后的值 |
Min |
求较小的一个 |
Truncate |
求数字的整数部分 |
Sqrt |
求平方根 |
PI |
返回PI值 |
Floor |
求小于或等于该数的最大整数 |
Cos |
求余弦值 |
Ceiling |
求大于或等于该数的最小整数 |
Sin |
求正弦值 |
Exp |
求e的指定次幂 |
Tan |
求正切值 |
结构化异常处理(try,catch,finally,throw):
1.异常结构至少有一个try或一个catch或一个finally。try块中一般是较易出错或要求执行特殊资源释放的代码块。
2.catch块如果有多个,那么系统捕获到异常后会从上向下匹配。匹配成功,就将捕获到的异常对象赋值给这个catch后面的异常对象,并执行此catch块里的代码。执行后不再执行其他catch块。必须先捕捉派生类异常,然后再捕捉基型异常。不用优先捕捉Exceptin类异常。
3.finally块里的代码最终都会被执行。里面一般放置一些重要资源的释放和清理代码。比如文件资源、窗口资源、数据连接、网络连接等。
4.要捕获多个异常,可以使用嵌套异常结构。或配合使用判断和抛出异常throw。
5.自定义异常类可以提供更多信息和功能,一般要写成和系统异常类类似的名字,即以Exception结尾 。选择哪个系统异常类继承需要考虑好,尽量不要引起误解。
6.异常的一些属性可以提供很多调试信息
TargetSite--获取引发当前异常的方法及该方法所在的类和成员属性等信息
StackTrace--确定错误发生位置的堆栈跟踪,包含源文件名和程序行号,对调试很有帮助
HelpLink--可用于保存某个帮助文件的URL或URN,该帮助文件通常提供异常的大量信息
Data--可以以键值对形式保存任意的数据,其类型为IDictionary,方便后续处理
using的三个用法:
1.using + 命名空间名字
2.using别名。using + 别名 = 包括详细命名空间信息的具体的类型。
这种做法有个好处就是当同一个cs引用了两个不同的命名空间,但两个命名空间都包括了一个相同名字的类型的时候。当需要用到这个类型的时候,就每个地方都要用详细命名空间的办法来区分这些相同名字的类型。而用别名的方法会更简洁,用到哪个类就给哪个类做别名声明就可以了。注意:并不是说两个名字重复,给其中一个用了别名,另外一个就不需要用别名了,如果两个都要使用,则两个都需要用using来定义别名的。
{// the code using cls1, cls2} // call the Dispose on cls1 and cls2
多线程(Thread):
1.创建线程的方式是:Thread ThreadName=new Thread(new ThreadStart(方法名))
2.可以为线程指定由ThreadPriority枚举定义的优先级,不过优先级设置有时不起作用,比如belownormal 有时比normal执行的快
3.线程的状态有:Aborted、AbortRequested、Background、Running、Stopped、StopRequested、Suspended、Unstarted、WaitSleepJoin。主线程退出时会自动关闭后台线程,否则需要手动关闭。
4.线程的同步基元有:Monitor、Mutex、ReaderWriterLock、Semaphore、AutoResetEvent、ManualResetEvent。ReaderWriterLock在锁定资源写访问频率较低、持续时间也比较短的情况下的性能最好。AutoResetEvent事件在发送给等待线程以后,该事件会在终止时自动重置,而ManualResetEvent必须手动重置。
{
static string buffer = "0";
private const long Max = 1000000;
//锁对象,起到一把锁的作用
private object myLock = new object();
static void Main(string[] args)
{
Program test = new Program();
Thread threadA = new Thread(new ThreadStart(test.ThreadADoWork));
threadA.Start();
Thread threadB = new Thread(new ThreadStart(test.ThreadBDoWork));
threadB.Start();
threadA.Join();
threadB.Join();
Console.WriteLine("结果是:{0}", buffer);
}
void ThreadADoWork()
{
for (long i = 0; i < Max / 2; i++)
lock (myLock) { buffer = (long.Parse(buffer) + i).ToString(); }
}
void ThreadBDoWork()
{
for (long i = Max / 2; i <= Max; i++)
lock (myLock) { buffer = (long.Parse(buffer) + i).ToString(); }
}
}
5.Lock实际是封装了Monitor的操作,使用更简单。Monitor还有个TryEnter的方法,区别是不管得不得到锁都立即返回,不会一直等待。
try
{
//锁定代码块
}
Finally
{
Monitor.Exit(buffer)//放在finally里可以保证出现异常时也能释放锁
}
********* 等同于 *************************
Lock(buffer){//锁定代码块}
6.AutoResetEvent常用的方法有:Set、Reset、WaitOne、WaitAny、WaitAll
{
//创建一个事件变量,初始状态为无信号
static AutoResetEvent myResetEvent = new AutoResetEvent(false);
static int number;
static void Main()
{
//建立一个读数字线程.
Thread myReaderThread = new Thread(new ThreadStart(MyReadThreadProc));
myReaderThread.Name = "读线程";
myReaderThread.Start();
for (int i = 1; i <= 5; i++)
{
Console.WriteLine("写线程写的值是: {0}", i);
//写入整型变量
number = i;
//发送一个信号给读线程
myResetEvent.Set();
//给读线程一个操作时间,好比红绿灯切换要给车辆通过的时间
Thread.Sleep(1);
}
//终止读线程
myReaderThread.Abort();
}
static void MyReadThreadProc()
{
while (true)
{
//停止往下执行直到信号到来
myResetEvent.WaitOne();
//信号来了后执行的读操作
Console.WriteLine("{0} 读到的值是: {1}", Thread.CurrentThread.Name, number);
}
}
}
{
//建立事件数组
AutoResetEvent[] autoEvents;
public Person()
{
//初始化事件数组
autoEvents = new AutoResetEvent[]
{
new AutoResetEvent(false),
new AutoResetEvent(false),
new AutoResetEvent(false)
};
}
public void GetCar()
{
Console.WriteLine("捡到宝马");
autoEvents[0].Set();//发送得到车子信号
}
public void GeHome()
{
Console.WriteLine("赚到房子");
autoEvents[1].Set();//发送得到房子信号
}
public void GetWife()
{
Console.WriteLine("骗到老婆");
autoEvents[2].Set();//发送得到妻子信号
}
public void ShowHappiness()
{
Console.WriteLine("生活原来如此美好");
}
static void Main()
{
//进入主线程
Person p = new Person();
//设置三个线程分别来得到房子、车子、妻子
Thread th1 = new Thread(new ThreadStart(p.GetCar)); th1.Start();
Thread th2 = new Thread(new ThreadStart(p.GeHome)); th2.Start();
Thread th3 = new Thread(new ThreadStart(p.GetWife)); th3.Start();
//等待事件数组中所有信号到齐,才会继续往下执行
AutoResetEvent.WaitAll(p.autoEvents);
p.ShowHappiness();
}
}
7.系统创建线程和切换线程会降低性能,使用线程池自动管理线程可以改善性能。可以通过IsThreadPoolThread判断一个线程是否是线程池里的线程。由于线程池的特性,它不适合以下需求:
a.需要前台线程;
b.需要设定线程的优先级;
c.由于线程池有最大线程数限制,线程池里的线程任务不能是会长时间被阻止的;
d.需要具有与线程关联的属性或使某一线程专用于某一任务时。
创建线程池线程队列的语法是:ThreadPool.QueueUserWorkItem(WaitCallback callback,Object state);其中callback是要执行的方法代理,state是可选项,代表方法需要引用的参数
{
static void Main(string[] args)
{
Console.WriteLine("以下开始将工作排入线程管理队列");
Console.WriteLine("******************************");
for (int i = 0; i < 4; i++)
{
WaitCallback myWaitCallback = new WaitCallback(WorkItem);
ThreadPool.QueueUserWorkItem(myWaitCallback, i + 1);
Thread.Sleep(1000);
}
Console.WriteLine("******************************");
for (int i = 0; i < 4; i++)
{
WaitCallback myWaitCallback = new WaitCallback(LongWorkItem);
ThreadPool.QueueUserWorkItem(myWaitCallback, i + 1);
Thread.Sleep(1000);
}
Console.ReadLine();
}
static void WorkItem(object i)
{
string itemName;
itemName = "使用的线程编号Thread ID="
+ Thread.CurrentThread.GetHashCode();
Console.WriteLine("这是队列中第1组第{0}个工作,{1}",i,itemName);
Console.WriteLine("此工作线程结束!");
//由于没有暂停,执行速度够快,线程池只使用了一个线程先后完成四个工作
}
static void LongWorkItem(object i)
{
string itemName;
itemName = "使用的线程编号Thread ID="
+ Thread.CurrentThread.GetHashCode();
Console.WriteLine("这是队列中第2组第{0}个工作,{1}", i, itemName);
Console.WriteLine("此工作线程暂停10秒!");
//由于暂停了10秒,线程池为4个工作创建四个线程
Thread.Sleep(10000);
}
}
8.静态方法Synchronized可以创建线程安全的集合,但是容易造成死锁。适合用于集合对象较少的项目。最佳方案是使用SyncRoot
{
//线程访问的集合
static ArrayList myAL = new ArrayList();
//线程访问的集合安全包装
static ArrayList sfAL = ArrayList.Synchronized(myAL);
public static void Main()
{
Thread T1 = new Thread(new ThreadStart(Write));
T1.Start();
Thread T2 = new Thread(new ThreadStart(Read));
T2.Start();
}
public static void Write()
{
while (true)
{
//锁定集合进行操作
lock (myAL.SyncRoot)
{
myAL.Add(DateTime.Now.ToString());
}
}
}
public static void Read()
{
while (true)
{
//锁定集合进行操作
lock (sfAL.SyncRoot)
{
foreach (object obj in myAL)
Console.WriteLine("读: {0} ", obj.ToString());
}
}
}
}
9.Abort方法可以正常退出线程。Interrupt方法是强制退出线程,可能产生一个ThreadInterruptedException的异常
集合对象(Array、ArrayList、Hashtable、):
Array本身是抽象类,只能通过数组的静态方法CreateInstance创建实例。该方法可以创建多维数组,甚至下标不从零开始的数组
{ //第一维2个元素,第二维3个元素
int[] lengths = new int[2] { 2, 3 };
//第一维下标从1开始,第二维下标从2开始
int[] starts = new int[2] { 1, 2 };
Array name = Array.CreateInstance(typeof(int), lengths, starts);
}
ArrayList可以定义非固定长度的数组,可以同时添加、插入或移除一个范围内的元素,但是只能是下标从零开始的一维数组,执行效率也比数组低。ArrayList有个容量的概念,容量表示能装多少元素,元素个数表示现在已装了多少元素。当元素个数超过时会自动增加容量,一般是自动增加一倍。
DictionaryEntry可以类似Hashtable,存放键/值对成员,由于Hashtable 只能通过键的哈希代码对元素进行排序,所以一般要结合ArrayList来间接排序
{
//创建Hashtable集合
Hashtable ht = new Hashtable();
//向集合中添加键/值对
ht.Add("BENZ", "奔驰");
ht.Add("BMW", "宝马");
//假如集合中没有键"CADILLAC"则添加元素
if (!ht.Contains("CADILLAC")) ht.Add("CADILLAC", "凯迪拉克");
//假如集合中没有值"丰田"则添加元素
if (!ht.ContainsValue("丰田")) ht.Add("TOYOTA", "丰田");
//显示元素个数
Console.WriteLine("目前元素个数:{0}", ht.Count);
Console.WriteLine("目前元素有:");
//遍历元素
foreach (DictionaryEntry de in ht)
Console.WriteLine("Key=\"{0}\"\tValue=\"{1}\"", de.Key, de.Value);
//删除元素
ht.Remove("BENZ");
//利用ArrayList对Hashtable键集合进行排序
ArrayList aKeys = new ArrayList(ht.Keys);
aKeys.Sort();
//遍历元素
Console.WriteLine("排序后元素:");
foreach (string key in aKeys)
Console.WriteLine("Key=\"{0}\"\tValue=\"{1}\"", key, ht[key].ToString());
//删除所以集合元素
ht.Clear();
}
Icollection接口包含Count、IsSynchronized、SyncRoot、CopyTo
Ienumerable接口包含Current、MoveNext、Reset
Ilist接口包含IsReadOnly、IsFixedSize、Item、Add、Clear、Contains、Indexof、Insert、Remove、RemoveAt
自定义集合类必须实现继承接口的所有成员方法。如果继承了Ilist,还需重写IConnection和IEnumerable里的方法(由于Ilist继承自这两个接口)
栈Stack实现先入后出(Push、Pop),队列Queue实现先入先出(Dequeue、Enqueue)
SortedList是哈希表和数组的杂交,其元素既可通过其键来访问,也可通过索引访问(GetKey、GetByIndex、IndexOfKey、IndexOfValue)
字符串集合StringCollection和StringDictionary的元素不是Object类型,而是string类型
泛型:
1.常用的泛型约束有:T:struct、T:class、T:类名、T:接口名、T:U。可以同时设定多个约束,用逗号分隔。
组件技术
使用反射之前需使用System.Reflection下的Assembly类的Load和LoadFrom方法加载程序集。Load方法加载的话,需先将程序集文件dll复制到当前项目exe文件所在目录下(如果是强名称程序集就不必复制了)。加载后,用GetType或GetTypes获得加载程序集的类型。获得类型后,用Activator.CreateInstance()重建类型对象。现在就可用Type类的一些方法获取此类型对象对应的属性或执行对象的方法了。
2 {
3 class Person
4 {
5 public string name;
6 private static int age;
7 public Person(string nm, int ag)
8 { name = nm; age = ag; }
9 public string Name
10 {
11 get { return name; }
12 set { name = value; }
13 }
14 public int Age
15 {
16 get { return age; }
17 set { age = value; }
18 }
19 public void Say(string msg)
20 { Console.WriteLine("{0}说:{1}", name, msg); }
21 public void SayHello()
22 { Console.WriteLine("{0}说:Hello!", name); }
23 }
24 static void Main(string[] args)
25 {
26 //获取类型
27 Type t = typeof(Person);
28 //创建类型对应的对象
29 object obj = System.Activator.CreateInstance(t, new object[] { "翠花", 18 });
30 //获取类型的公共字段
31 FieldInfo[] fields = t.GetFields();
32 foreach (FieldInfo tf in fields)
33 { Console.WriteLine("字段{0},值:{1}", tf.Name, tf.GetValue(obj)); }
34 //获取类型的公共属性
35 PropertyInfo[] properties = t.GetProperties();
36 foreach (PropertyInfo tp in properties)
37 {
38 Console.WriteLine("属性{0}:{1}", tp.Name, tp.GetValue(obj, null));
39 }
40 //设置Name属性的值
41 PropertyInfo p = t.GetProperty("Name");
42 p.SetValue(obj, "小芳", null);
43 //获取类型方法信息
44 MethodInfo[] methods = t.GetMethods();
45 foreach (MethodInfo tm in methods)
46 {
47 Console.WriteLine("方法{0}", tm.Name);
48 }
49 //调用类型方法
50 MethodInfo m = t.GetMethod("Say");
51 //如果调用的方法没有参数,则Invoke方法的第二个参数为null
52 m.Invoke(obj, new object[] { "我是一朵花" });
53 m = t.GetMethod("SayHello");
54 m.Invoke(obj, null);
55 }
56 }
调用API需使用DllImportAttribute标示,同时可使用EntryPoint标记导入方法的名称
[DllImport("user32.dll", EntryPoint = "MessageBoxA", CharSet = CharSet.Auto)]
如果调用的COM组件不是.Net系统的,则需先用tlbimp.exe工具转换:
tlbimp comsample.dll /out: comsample_clw.dll
序列化(Serializable):
1.序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据。
2.如果采用BinaryFormatter类或者SoapFormatter类来实现序列化,则一定要给类加上Serializable属性。使用XmlSerializer 类序列化对象时可以不用这个属性另外,如果不想序列化某个字段,可以给其加上NonSerialized属性,这样在序列化时就不会保存这个这个字段的值了,反序列化时该数据将丢失。XmlIgnore属性,和NonSerializable属性类似。Soap命名空间需要手动添加引用
2 using System.IO;
3 using System.Runtime.Serialization.Formatters.Binary;
4 using System.Runtime.Serialization.Formatters.Soap;
5 using System.Xml.Serialization;
6
7 namespace MySerializeDemo
8 {
9 public class Program
10 {
11 [Serializable]
12 public class ClsSerializable
13 {
14 public int intNumber = 254;
15 public string strDemo = "Hello world!";
16 //[NonSerialized]
17 public long lngNumber = 123456;
18 }
19
20 static void Main(string[] args)
21 {
22 ClsSerializable obj = new ClsSerializable();
23 Console.WriteLine("========使用BinaryFormatter类进行序列化和反序列化。====");
24 BinarySerialize(obj);
25 BinaryDeserialize("C:\\MyObject.dat");
26 Console.WriteLine("========使用SoapFormatter类进行序列化和反序列化。====");
27 SOAPSerialize(obj);
28 SOAPDeserialize("C:\\MyObject.soap");
29 Console.WriteLine("========使用XmlSerializer类进行序列化和反序列化。====");
30 XMLSerialize(obj);
31 XMLDeserialize("C:\\MyObject.xml");
32 }
33 // 二进制序列化对象
34 public static void BinarySerialize(ClsSerializable obj)
35 {
36 using (FileStream stream = new FileStream("C:\\MyObject.dat", FileMode.Create, FileAccess.Write))
37 {
38 BinaryFormatter formater = new BinaryFormatter();
39 formater.Serialize(stream, obj);
40 Console.WriteLine("对象已经被序列化。" + obj.ToString());
41 }
42 }
43 // 二进制反序列化
44 public static void BinaryDeserialize(string fileName)
45 {
46 using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
47 {
48 BinaryFormatter formater = new BinaryFormatter();
49 ClsSerializable obj = (ClsSerializable)formater.Deserialize(stream);
50 Console.WriteLine("对象已经被反序列化。" + obj.ToString());
51 }
52 }
53 // SOAP序列化对象
54 public static void SOAPSerialize(ClsSerializable obj)
55 {
56 using (FileStream stream = new FileStream("C:\\MyObject.soap", FileMode.Create, FileAccess.Write))
57 {
58 SoapFormatter formater = new SoapFormatter();
59 formater.Serialize(stream, obj);
60 Console.WriteLine("对象已经被序列化。" + obj.ToString());
61 }
62 }
63 // SOAP反序列化
64 public static void SOAPDeserialize(string fileName)
65 {
66 using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
67 {
68 SoapFormatter formater = new SoapFormatter();
69 ClsSerializable obj = (ClsSerializable)formater.Deserialize(stream);
70 Console.WriteLine("对象已经被反序列化。" + obj.ToString());
71 }
72 }
73 // XML序列化
74 public static void XMLSerialize(ClsSerializable obj)
75 {
76 using (FileStream stream = new FileStream("C:\\MyObject.xml", FileMode.Create, FileAccess.Write))
77 {
78 XmlSerializer serializer = new XmlSerializer(typeof(ClsSerializable));
79 serializer.Serialize(stream, obj);
80 Console.WriteLine("对象已经被序列化。" + obj.ToString());
81 }
82 }
83 // XML反序列化
84 public static void XMLDeserialize(string fileName)
85 {
86 using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
87 {
88 XmlSerializer serializer = new XmlSerializer(typeof(ClsSerializable));
89 ClsSerializable obj = (ClsSerializable)serializer.Deserialize(stream);
90 Console.WriteLine("对象已经被反序列化。" + obj.ToString());
91 }
92 }
93 }
94 }
3.实现Iserializable接口可以自定义序列化行为,继承这个接口类必须实现方法GetObjectData()和构造函数
2 using System.IO;
3 using System.Runtime.Serialization.Formatters.Binary;
4 using System.Runtime.Serialization;
5
6 namespace MySerializeDemo
7 {
8 public class Program
9 {
10 [Serializable]
11 public class ClsSerializable : ISerializable
12 {
13 public int intNumber = 254;
14 public string message = "Hello world!";
15 public long lngNumber = 123456;
16 public int[] intArrayX = new int[3];
17 public int[] intArrayY = new int[3];
18
19 public ClsSerializable()
20 { ChangeMemberValue(); }
21 public void GetObjectData
22 (SerializationInfo info, StreamingContext context)
23 {
24 info.AddValue("intNumber", intNumber);
25 info.AddValue("message", message);
26 info.AddValue("intArrayX", intArrayX);
27 }
28 public ClsSerializable
29 (SerializationInfo info, StreamingContext context)
30 {
31 intNumber = (int)info.GetValue("intNumber", typeof(int));
32 message = (string)info.GetValue("message", typeof(string));
33 intArrayX = (int[])info.GetValue("intArrayX", typeof(int[]));
34 }
35 void ChangeMemberValue()
36 {
37 for (int i = 0; i < 3; i++)
38 { intArrayX[i] = i * 100; }
39 intNumber = 2000;
40 message = "修改过的字符串";
41 }
42 }
43
44 static void Main(string[] args)
45 {
46 ClsSerializable myClsIS = new ClsSerializable();
47 //序列化
48 FileStream stream = new FileStream("C:\\MyObject.bin", FileMode.Create, FileAccess.Write);
49 BinaryFormatter formater = new BinaryFormatter();
50 formater.Serialize(stream, myClsIS);
51 myClsIS = null;
52 stream.Close();
53 //反序列化
54 stream = new FileStream("C:\\MyObject.bin", FileMode.Open);
55 formater = new BinaryFormatter();
56 myClsIS = (ClsSerializable)formater.Deserialize(stream);
57 stream.Close();
58
59 Console.WriteLine(myClsIS.message);
60 Console.WriteLine(myClsIS.intNumber);
61 Console.WriteLine(myClsIS.lngNumber);
62 for (int i = 0; i < 3; i++)
63 {
64 Console.WriteLine(myClsIS.intArrayX[i]);
65 }
66 }
67 }
68 }
69
4.对象的序列化属性不能被继承,也就是说必须在子类明确标注[Serialized],还必须实现GetObjectData()和构造函数
5.对于无法序列化的数据,在反序列化时会丢失数据。为了解决这个问题,可以继承IDeserializationCallback接口,实现方法OnDeserialization还原正确数据
6.XML序列化仅将对象的公共字段、属性值和只读集合序列化为XML流,不包括类型信息,所以要完全序列化,请使用二进制序列化。除非二进制序列化无法穿越防火墙,才考虑使用XML序列化
其他:
1.this关键字只能用在方法、属性、索引器的代码中,不能在构造和析构方法内部使用。this不能引用静态方法或属性。
2.#region 可把一组代码标注为一个有名称的可伸缩的块
3.base关键字用于指定子类调用父类哪一个构造方法,以及调用父类成员
4.default关键字 解决类型参数的默认值 如T para=default(T)表示T是引用类型para的默认值为null,否则为数值0
5.fixed块里的被引用的变量将固定在内存中,不会在重组时被移动
6.unsafe 关键字表示不安全上下文,该上下文是任何涉及指针的操作所必需的
volatile
yield
explicit
extern
implicit
stackalloc
groble