2006年5月8日

解析C语言中的sizeof

一、sizeof的概念 
  sizeof是C语言的一种单目操作符,如C语言的其他操作符++、--等。它并不是函数。sizeof操作符以字节形式给出了其操作数的存储大小。操作数可以是一个表达式或括在括号内的类型名。操作数的存储大小由操作数的类型决定。 

二、sizeof的使用方法 
  1、用于数据类型 

  sizeof使用形式:sizeof(type) 

  数据类型必须用括号括住。如sizeof(int)。 

  2、用于变量 

  sizeof使用形式:sizeof(var_name)或sizeof var_name 

  变量名可以不用括号括住。如sizeof (var_name),sizeof var_name等都是正确形式。带括号的用法更普遍,大多数程序员采用这种形式。 

  注意:sizeof操作符不能用于函数类型,不完全类型或位字段。不完全类型指具有未知存储大小的数据类型,如未知存储大小的数组类型、未知内容的结构或联合类型、void类型等。 

  如sizeof(max)若此时变量max定义为int max(),sizeof(char_v) 若此时char_v定义为char char_v [MAX]且MAX未知,sizeof(void)都不是正确形式。 

三、sizeof的结果 
  sizeof操作符的结果类型是size_t,它在头文件

中typedef为unsigned int类型。该类型保证能容纳实现所建立的最大对象的字节大小。 

  1、若操作数具有类型char、unsigned char或signed char,其结果等于1。 

  ANSI C正式规定字符类型为1字节。 

  2、int、unsigned int 、short int、unsigned short 、long int 、unsigned long 、float、double、long double类型的sizeof 在ANSI C中没有具体规定,大小依赖于实现,一般可能分别为2、2、2、2、4、4、4、8、10。 

  3、当操作数是指针时,sizeof依赖于编译器。例如Microsoft C/C++7.0中,near类指针字节数为2,far、huge类指针字节数为4。一般Unix的指针字节数为4。 

  4、当操作数具有数组类型时,其结果是数组的总字节数。 

  5、联合类型操作数的sizeof是其最大字节成员的字节数。结构类型操作数的sizeof是这种类型对象的总字节数,包括任何垫补在内。 

  让我们看如下结构: 

  struct {char b; double x;} a; 

  在某些机器上sizeof(a)=12,而一般sizeof(char)+ sizeof(double)=9。 

  这是因为编译器在考虑对齐问题时,在结构中插入空位以控制各成员对象的地址对齐。如double类型的结构成员x要放在被4整除的地址。 

  6、如果操作数是函数中的数组形参或函数类型的形参,sizeof给出其指针的大小。 

四、sizeof与其他操作符的关系 
  sizeof的优先级为2级,比/、%等3级运算符优先级高。它可以与其他操作符一起组成表达式。如i*sizeof(int);其中i为int类型变量。 

五、sizeof的主要用途 
  1、sizeof操作符的一个主要用途是与存储分配和I/O系统那样的例程进行通信。例如: 

  void *malloc(size_t size), 

  size_t fread(void * ptr,size_t size,size_t nmemb,FILE * stream)。 

  2、sizeof的另一个的主要用途是计算数组中元素的个数。例如: 

  void * memset(void * s,int c,sizeof(s))。 

六、建议 
  由于操作数的字节数在实现时可能出现变化,建议在涉及到操作数字节大小时用sizeof来代替常量计算。
本文主要包括二个部分,第一部分重点介绍在VC中,怎么样采用sizeof来求结构的大小,以及容易出现的问题,并给出解决问题的方法,第二部分总结出VC中sizeof的主要用法。

1、 sizeof应用在结构上的情况

请看下面的结构:

struct MyStruct

{

double dda1;

char dda;

int type

};

对结构MyStruct采用sizeof会出现什么结果呢?sizeof(MyStruct)为多少呢?也许你会这样求:

sizeof(MyStruct)=sizeof(double)+sizeof(char)+sizeof(int)=13

但是当在VC中测试上面结构的大小时,你会发现sizeof(MyStruct)为16。你知道为什么在VC中会得出这样一个结果吗?

其实,这是VC对变量存储的一个特殊处理。为了提高CPU的存储速度,VC对一些变量的起始地址做了“对齐”处理。在默认情况下,VC规定各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。下面列出常用类型的对齐方式(vc6.0,32位系统)。

类型
对齐方式(变量存放的起始地址相对于结构的起始地址的偏移量)

Char
偏移量必须为sizeof(char)即1的倍数

int
偏移量必须为sizeof(int)即4的倍数

float
偏移量必须为sizeof(float)即4的倍数

double
偏移量必须为sizeof(double)即8的倍数

Short
偏移量必须为sizeof(short)即2的倍数

 

各成员变量在存放的时候根据在结构中出现的顺序依次申请空间,同时按照上面的对齐方式调整位置,空缺的字节VC会自动填充。同时VC为了确保结构的大小为结构的字节边界数(即该结构中占用最大空间的类型所占用的字节数)的倍数,所以在为最后一个成员变量申请空间后,还会根据需要自动填充空缺的字节。

下面用前面的例子来说明VC到底怎么样来存放结构的。

struct MyStruct

{

double dda1;

char dda;

int type

};

为上面的结构分配空间的时候,VC根据成员变量出现的顺序和对齐方式,先为第一个成员dda1分配空间,其起始地址跟结构的起始地址相同(刚好偏移量0刚好为sizeof(double)的倍数),该成员变量占用sizeof(double)=8个字节;接下来为第二个成员dda分配空间,这时下一个可以分配的地址对于结构的起始地址的偏移量为8,是sizeof(char)的倍数,所以把dda存放在偏移量为8的地方满足对齐方式,该成员变量占用sizeof(char)=1个字节;接下来为第三个成员type分配空间,这时下一个可以分配的地址对于结构的起始地址的偏移量为9,不是sizeof(int)=4的倍数,为了满足对齐方式对偏移量的约束问题,VC自动填充3个字节(这三个字节没有放什么东西),这时下一个可以分配的地址对于结构的起始地址的偏移量为12,刚好是sizeof(int)=4的倍数,所以把type存放在偏移量为12的地方,该成员变量占用sizeof(int)=4个字节;这时整个结构的成员变量已经都分配了空间,总的占用的空间大小为:8+1+3+4=16,刚好为结构的字节边界数(即结构中占用最大空间的类型所占用的字节数sizeof(double)=8)的倍数,所以没有空缺的字节需要填充。所以整个结构的大小为:sizeof(MyStruct)=8+1+3+4=16,其中有3个字节是VC自动填充的,没有放任何有意义的东西。

下面再举个例子,交换一下上面的MyStruct的成员变量的位置,使它变成下面的情况:

struct MyStruct

{

char dda;

double dda1;

int type

};

这个结构占用的空间为多大呢?在VC6.0环境下,可以得到sizeof(MyStruc)为24。结合上面提到的分配空间的一些原则,分析下VC怎么样为上面的结构分配空间的。(简单说明)

struct MyStruct

{

char dda;//偏移量为0,满足对齐方式,dda占用1个字节;

double dda1;//下一个可用的地址的偏移量为1,不是sizeof(double)=8

//的倍数,需要补足7个字节才能使偏移量变为8(满足对齐

//方式),因此VC自动填充7个字节,dda1存放在偏移量为8

//的地址上,它占用8个字节。

int type;//下一个可用的地址的偏移量为16,是sizeof(int)=4的倍

//数,满足int的对齐方式,所以不需要VC自动填充,type存

//放在偏移量为16的地址上,它占用4个字节。

};//所有成员变量都分配了空间,空间总的大小为1+7+8+4=20,不是结构

//的节边界数(即结构中占用最大空间的类型所占用的字节数sizeof

//(double)=8)的倍数,所以需要填充4个字节,以满足结构的大小为

//sizeof(double)=8的倍数。

 

所以该结构总的大小为:sizeof(MyStruc)为1+7+8+4+4=24。其中总的有7+4=11个字节是VC自动填充的,没有放任何有意义的东西。

 

VC对结构的存储的特殊处理确实提高CPU存储变量的速度,但是有时候也带来了一些麻烦,我们也屏蔽掉变量默认的对齐方式,自己可以设定变量的对齐方式。

VC中提供了#pragma pack(n)来设定变量以n字节对齐方式。n字节对齐就是说变量存放的起始地址的偏移量有两种情况:第一、如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式,第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。结构的总大小也有个约束条件,分下面两种情况:如果n大于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数;

否则必须为n的倍数。下面举例说明其用法。

#pragma pack(push) //保存对齐状态

#pragma pack(4)//设定为4字节对齐

struct test

{

char m1;

double m4;

int m3;

};

#pragma pack(pop)//恢复对齐状态

以上结构的大小为16,下面分析其存储情况,首先为m1分配空间,其偏移量为0,满足我们自己设定的对齐方式(4字节对齐),m1占用1个字节。接着开始为m4分配空间,这时其偏移量为1,需要补足3个字节,这样使偏移量满足为n=4的倍数(因为sizeof(double)大于n),m4占用8个字节。接着为m3分配空间,这时其偏移量为12,满足为4的倍数,m3占用4个字节。这时已经为所有成员变量分配了空间,共分配了16个字节,满足为n的倍数。如果把上面的#pragma pack(4)改为#pragma pack(16),那么我们可以得到结构的大小为24。(请读者自己分析)

2、 sizeof用法总结

在VC中,sizeof有着许多的用法,而且很容易引起一些错误。下面根据sizeof后面的参数对sizeof的用法做个总结。

A. 参数为数据类型或者为一般变量。例如sizeof(int),sizeof(long)等等。这种情况要注意的是不同系统系统或者不同编译器得到的结果可能是不同的。例如int类型在16位系统中占2个字节,在32位系统中占4个字节。

B. 参数为数组或指针。下面举例说明.

int a[50]; //sizeof(a)=4*50=200; 求数组所占的空间大小

int *a=new int[50];// sizeof(a)=4; a为一个指针,sizeof(a)是求指针

//的大小,在32位系统中,当然是占4个字节。

C. 参数为结构或类。Sizeof应用在类和结构的处理情况是相同的。但有两点需要注意,第一、结构或者类中的静态成员不对结构或者类的大小产生影响,因为静态变量的存储位置与结构或者类的实例地址无关。

第二、没有成员变量的结构或类的大小为1,因为必须保证结构或类的每一

个实例在内存中都有唯一的地址。

下面举例说明,

Class Test{int a;static double c};//sizeof(Test)=4.

Test *s;//sizeof(s)=4,s为一个指针。

Class test1{ };//sizeof(test1)=1;

D. 参数为其他。下面举例说明。

int func(char s[5]);

{

cout<
//数的参数在传递的时候系统处理为一个指针,所

//以sizeof(s)实际上为求指针的大小。

return 1;

}

sizeof(func(“1234”))=4//因为func的返回类型为int,所以相当于

//求sizeof(int).

 

以上为sizeof的基本用法,在实际的使用中要注意分析VC的分配变量的分配策略,这样的话可以避免一些错误。

posted @ 2006-05-08 17:01 leithon 阅读(52) 评论(0) 编辑
 
posted @ 2006-05-08 16:59 leithon 阅读(4267) 评论(0) 编辑
 
程序员在编写应用程序的时候往往要将程序的某些数据存储在内存中,然后将其写入某个文件或是将它传输到网络中的另一台计算机上以实现通讯。这个将程序数据转化成能被存储并传输的格式的过程被称为"序列化"(Serialization),而它的逆过程则可被称为"反序列化"(Deserialization)。

  .Net框架对序列化机制具有非常好的支持,它提供了两个名字空间(namespace):System.Runtime.Serialization和System.Runtime.Serialization.Formatters以完成序列化机制的大部分功能。系列化这项技术可以应用在将程序产生的结果数据存储到文件系统中,但是它更主要的应用是在于.Net Remoting和Web服务的实现上。

  序列化机制的实现是依靠格式器(Formatter)而完成的,它是一个从System.Runtime.Serialization.IFormatter继承下来的类的对象。格式器完成了将程序数据转化到能被存储并传输的格式的工作,同时也完成了将数据转化回来的工作。.Net框架为程序员提供了两种类型的格式器,一种通常是应用于桌面类型的应用程序的,它一个是System.Runtime.Serialization.Formatters.Binary.BinaryFormatter类的对象,而另一种则更主要的应用于.Net Remoting和XML Web服务等领域的,它一个是System.Runtime.Serialization.Formatters.Soap.SoapFormatter类的对象。从它们的名称来看,我们不妨将它们分别称为二进制格式器和XML格式器。

  本文将从这两个格式器入手,先向大家介绍分别用它们如何实现序列化和反序列化,然后比较两种格式器的不同点。接着我会向大家介绍实现序列化对对象类型的一些要求,同时还要向大家介绍两种不同的序列化方式:基本序列化(Basic Serialization)和自定义序列化(Custom Serialization)。最后,我还会给大家介绍一个实例程序以加深大家对序列化机制的理解程度。

  一.二进制格式器(Binary Formatter) vs XML格式器(XML Formatter):

  下面我先向大家介绍两种不同的格式器,分别用它们如何实现序列化机制和反序列化机制,请看下面的代码:

#region Binary Serializers
public static System.IO.MemoryStream SerializeBinary(object request) {
 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter serializer =
  new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
 System.IO.MemoryStream memStream = new System.IO.MemoryStream();
 serializer.Serialize(memStream, request);
 return memStream;
}

public static object DeSerializeBinary(System.IO.MemoryStream memStream) {
 memStream.Position=0;
 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter deserializer =
  new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
 object newobj = deserializer.Deserialize(memStream);
 memStream.Close();
 return newobj;
}
#endregion

#region XML Serializers
public static System.IO.MemoryStream SerializeSOAP(object request) {
 System.Runtime.Serialization.Formatters.Soap.SoapFormatter serializer =
  new System.Runtime.Serialization.Formatters.Soap.SoapFormatter();
 System.IO.MemoryStream memStream = new System.IO.MemoryStream();
 serializer.Serialize(memStream, request);
 return memStream;
}

public static object DeSerializeSOAP(System.IO.MemoryStream memStream) {
 object sr;
 System.Runtime.Serialization.Formatters.Soap.SoapFormatter deserializer =
  new System.Runtime.Serialization.Formatters.Soap.SoapFormatter();
 memStream.Position=0;
 sr = deserializer.Deserialize(memStream);
 memStream.Close();
 return sr;
}
#endregion

  从上面的代码我们可以发现无论运用哪种格式器,其基本的过程都是一样的,而且都是非常容易实现的,唯一的不同就是定义格式器的类型不同。不过在实际的应用中,二进制格式器往往应用于一般的桌面程序和网络通讯程序中,而XML格式器禀承了XML技术的优点,大多数被应用于.Net Remoting和XML Web服务等领域。下面我们来分析一下两种格式器各自的优点。

  二进制序列化的优点:

  1. 所有的类成员(包括只读的)都可以被序列化;

  2. 性能非常好。

  XML序列化的优点:

  1. 互操作性好;

  2. 不需要严格的二进制依赖;

  3. 可读性强。

  通过分析上面的代码,我们知道了选择二进制序列化的方式还是选择XML序列化的方式仅仅是对不同的格式器进行选择而已。你可以根据实际的需要选择相应的格式器完成序列化和反序列化工作。同时请注意,代码中的序列化函数和反序列化函数仅仅是在调用Serialize()和Deserialize()这两个核心函数上产生了差别,即它们的参数不同。因此以上的代码完成了一些最最基本但是很重要的功能,你可以将它们运用在你的程序中,或是将其进行适当扩充以满足程序的特定需要。

二.序列化机制对类的要求:

  如果你要对一个对象进行序列化,那么你必须将它的类型标记为[Serializable()],该操作是通过SerializableAttribute属性来实现的。将SerializableAttribute属性应用于一种数据类型可表明该数据类型的实例可以被序列化。如果正在序列化的对象图中的任何类型未应用SerializableAttribute属性,公共语言运行库则会引发SerializationException。默认情况下,类型中由SerializableAttribute标记的所有公共和私有字段都会进行序列化,除非该类型实现ISerializable接口来重写序列化进程(通过实现该接口我们便可以实现将在后面介绍的"自定义序列化")。默认的序列化进程会排除用NonSerializedAttribute属性标记的字段,即你可以将该类型标记为[NonSerialized()]以表明它是不可以被序列化的。如果可序列化类型的字段包含指针、句柄或其他某些针对于特定环境的数据结构,并且不能在不同的环境中以有意义的方式重建,则最好将NonSerializedAttribute属性应用于该字段。有关序列化的更多信息,请参阅System.Runtime.Serialization名字空间中的相关内容。

  下面我给大家介绍一个例子,以显示如何正确的运用SerializableAttribute属性和NonSerializedAttribute属性。该程序中运用到了XML格式器,不过同时给出了二进制格式器为参考(程序中将其用"//"标注),其实现的结果是一样的。该程序实现的功能是在序列化和反序列化操作前后测试对象因包含了[NonSerialized()]的字段而显示不同的屏幕打印结果。其代码如下:

 

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Soap;
//using System.Runtime.Serialization.Formatters.Binary;

public class Test {
 public static void Main() {
  // 创建一个新的测试对象
  TestSimpleObject obj = new TestSimpleObject();

  Console.WriteLine("Before serialization the object contains: ");
  obj.Print();

  // 创建一个文件"data.xml"并将对象序列化后存储在其中
  Stream stream = File.Open("data.xml", FileMode.Create);
  SoapFormatter formatter = new SoapFormatter();
  //BinaryFormatter formatter = new BinaryFormatter();

  formatter.Serialize(stream, obj);
  stream.Close();
  
  // 将对象置空
  obj = null;

  // 打开文件"data.xml"并进行反序列化得到对象
  stream = File.Open("data.xml", FileMode.Open);
  formatter = new SoapFormatter();
  //formatter = new BinaryFormatter();

  obj = (TestSimpleObject)formatter.Deserialize(stream);
  stream.Close();

  Console.WriteLine("");
  Console.WriteLine("After deserialization the object contains: ");
  obj.Print();
 }
}

// 一个要被序列化的测试对象的类
[Serializable()]
 public class TestSimpleObject {
 public int member1;
 public string member2;
 public string member3;
 public double member4;

 // 标记该字段为不可被序列化的
[NonSerialized()] public string member5;

public TestSimpleObject() {
 member1 = 11;
 member2 = "hello";
 member3 = "hello";
 member4 = 3.14159265;
 member5 = "hello world!";
}

public void Print() {
 Console.WriteLine("member1 = '{0}'", member1);
 Console.WriteLine("member2 = '{0}'", member2);
 Console.WriteLine("member3 = '{0}'", member3);
 Console.WriteLine("member4 = '{0}'", member4);
 Console.WriteLine("member5 = '{0}'", member5);
}
}

三.基本序列化(Basic Serialization) vs 自定义序列化(Custom Serialization):

  .Net框架为我们提供了两种方式的序列化:一种为基本序列化、另一种为自定义序列化。值得注意的是,序列化的方式和前面提到的序列化的格式是不同的概念。序列化的方式是指.Net框架将程序的数据转化为能被存储并传输的格式的实际过程,它是不管程序员运用了何种类型的格式器的(二进制格式器还是XML格式器)。而序列化的格式则指程序的数据是被转化成二进制格式了还是被转化成XML格式了。

  完成序列化的最简单的方法便是让.Net框架自动为我们完成整个过程,而我们不必去管它内部是如何具体实现的,这种方法便是前面提到的"基本序列化"。在这种方式下,我们需要做的仅仅是将类标记上[Serializable()]属性。然后.Net框架便调用该类的对象并将它转化为所需的格式。同时你还可以控制其中的某些字段不被序列化,方法就是前面所述的将该字段标记上[NonSerialized()]属性。这样,最最简单和基本的序列化工作就完成了,不过其内部是如何实现的你是不得而知的,同时你也不能进一步控制序列化过程的程序行为。

  如果你要获得对序列化的更大的控制权,那么你就得使用"自定义序列化"的方式。通过使用这种方式,你可以完全的控制类的哪些部分能被序列化而哪些部分不能,同时你还可以控制如何具体的进行序列化。运用该方式的好处就是能克服基本序列化所会遇到的问题。我们在运用基本序列化将一个类的对象序列化完毕并存储在文件中后,假设该对象原来有三个字段,如果此时该对象增加了一个字段,那么再将该对象从文件中反序列化出来时会发生字段数不一致的错误。这样的问题是基本序列化所不能解决的,只能运用自定义序列化的方式来解决。

  在介绍自定义序列化之前,我先给出介绍过程中所要用到的实例程序的代码。这是一个时间安排程序,其中要用到将不同的时间格式进行转化的操作。所以运用序列化的机制能很好的解决这个问题。

 

using System;
using System.Runtime.Serialization;

namespace SerializationSample {
[Serializable()]
 public class Schedule {
  protected System.DateTime start;
  protected System.DateTime end;
  // 每个时间间隔所要增加的毫秒数
  protected long interval;

  public System.DateTime Start {get{return start;}set{start=value;}}
  public System.DateTime End {get{return end;}set{end=value;}}
  public long Interval {get{return interval;}set{interval=value;}}
  public Schedule(System.DateTime Start, System.DateTime End, long Interval) {
   start=Start;
   end=End;
  interval=Interval;
}

// 如果已经到了结束的时间,则返回结束时间,否则返回下一次运行的时间
public System.DateTime NextRunTime {
 get {
  System.TimeSpan ts = new System.TimeSpan(end.Ticks-System.DateTime.Now.Ticks);
  if(ts.Milliseconds>0) {
   return System.DateTime.Now.AddMilliseconds(interval);
  } else {
   return end;
  }
 }
}
}
}

  自定义序列化:

  下面我就向大家介绍自定义序列化以及反序列化的具体过程。首先,程序的类必须实现System.Runtime.Serialization.ISerializable接口,该接口的功能就是允许对象控制其自己的序列化和反序列化过程。所以我们得重新定义上面的类:

[Serializable()]
public class ScheduleCustom : System.Runtime.Serialization.Iserializable

  接下来,我们必须对该接口调用GetObjectData()的实现,也即我们必须在上面的类中给出GetObjectData()的具体实现。其函数原型如下:

void GetObjectData(SerializationInfo info, StreamingContext context);

  上面的类中GetObjectData()的具体实现如下:

public void GetObjectData(SerializationInfo info,StreamingContext context) {
// 运用info对象来添加你所需要序列化的项
info.AddValue("start", start);
info.AddValue("end", end);
info.AddValue("interval", interval);
}

   然而对于这么一个简单的方法,读者可能不能理会到系列化带来的强大功能,所以下面我就给这个方法添加一些东西。如果在系列化过程中我们要查看类型为DateTime的"start"属性的输出的话,其结果会是.Net框架默认的格式:

  2002-12-19T14:09:13.3457440-07:00

  而对于没有.Net框架的用户,或是在其他时间区域内的用户而言,这么一个格式的时间可能是非常难以理解的,所以我们有必要将时间的格式转化为格林威治标准时间格式,于是修改GetObjectData()方法如下:

public void GetObjectData(SerializationInfo info,StreamingContext context) {
// 运用info对象来添加你所需要序列化的项
// 同时,将"start"和"end"属性的时间格式转化为格林威治标准时间格式
info.AddValue("start", System.TimeZone.CurrentTimeZone.ToUniversalTime(start));
info.AddValue("end", System.TimeZone.CurrentTimeZone.ToUniversalTime(end));
info.AddValue("interval", interval);
info.AddValue("timeformat", "utc");
}

  这样一来,我们在系列化过程中查看"start"属性时就会得到如下结果:

   8/19/2002 9:09:13 PM

  同时请注意我们在GetObjectData()方法中添加的一个名为"timeformat"的额外属性,通过它我们可以方便的知道系列化过程中所使用的时间格式。如果有兴趣的话,你还可以从System.Globalization.DateTimeFormatInfo这个名字空间中获取更多有关时间格式的信息。

  自定义反序列化:

  你可以通过调用一个自定义的构造函数来完成自定义反序列化的操作。该构造函数的定义如下:

public ScheduleCustom (SerializationInfo info,StreamingContext context);

  在上面的类中,我们的ScheduleCustom()方法将完成把时间格式从格林威治标准时间格式反序列化为当地时间的格式的操作,其函数实现如下:

public ScheduleCustom (SerializationInfo info,StreamingContext context) {
 this.start = info.GetDateTime("start").ToLocalTime();
 this.end = info.GetDateTime("end").ToLocalTime();
 this.interval = info.GetInt32("interval");
}

  在完成自定义序列化和自定义反序列化后,我们的时间安排程序变成了如下形式:

using System;
using System.Runtime.Serialization;

namespace SerializationSample {
[Serializable()]
 public class ScheduleCustom : System.Runtime.Serialization.ISerializable {
 protected System.DateTime start;
 protected System.DateTime end;
 // 每个时间间隔所要增加的毫秒数
 protected long interval;

 public System.DateTime Start {get{return start;}set{start=value;}}
 public System.DateTime End {get{return end;}set{end=value;}}
 public long Interval {get{return interval;}set{interval=value;}}

 public ScheduleCustom(System.DateTime Start, System.DateTime End, long Interval) {
  start=Start;
  end=End;
  interval=Interval;
 }

// 如果已经到了结束的时间,则返回结束时间,否则返回下一次运行的时间
public System.DateTime NextRunTime {
 get {
  System.TimeSpan ts = new System.TimeSpan(end.Ticks-System.DateTime.Now.Ticks);
  if(ts.Milliseconds>0) {
   return System.DateTime.Now.AddMilliseconds(interval);
  } else {
   return end;
  }
 }
}

public void GetObjectData(SerializationInfo info,StreamingContext context) {
 // 运用info对象来添加你所需要序列化的项
 // 同时,将"start"和"end"属性的时间格式转化为格林威治标准时间格式
 info.AddValue("start", System.TimeZone.CurrentTimeZone.ToUniversalTime(start));
 info.AddValue("end", System.TimeZone.CurrentTimeZone.ToUniversalTime(end));
 info.AddValue("interval", interval);
 info.AddValue("timeformat", "utc");
}

public ScheduleCustom (SerializationInfo info,StreamingContext context) {
 this.start = info.GetDateTime("start").ToLocalTime();
 this.end = info.GetDateTime("end").ToLocalTime();
 this.interval = info.GetInt32("interval");
 }
}
}

  四.总结:

  本文向大家介绍了.Net框架下系列化机制的一些基本概念和基本的运用方法,读者在读完本文后,应该对以下几个概念有个初步了解:二进制系列化、XML系列化、基本序列化和自定义系列化,并应能够完成一些基本的系列化应用。最后,希望大家能合理有效的运用系列化机制并发挥它的功效以更好地满足实际工作需要。

posted @ 2006-05-08 16:58 leithon 阅读(88) 评论(0) 编辑
 

<!--StartFragment-->//获得汉字的区位码
  byte[] array = new byte[2];
  array = System.Text.Encoding.Default.GetBytes("啊");

int i1 = (short)(array[0] - ''\0'');
  int i2 = (short)(array[1] - ''\0'');

//unicode解码方式下的汉字码
  array = System.Text.Encoding.Unicode.GetBytes("啊");
  i1 = (short)(array[0] - ''\0'');
  i2 = (short)(array[1] - ''\0'');


//unicode反解码为汉字
  string str = "4a55";
  string s1 = str.Substring(0,2);
  string s2 = str.Substring(2,2);


int t1 = Convert.ToInt32(s1,16);
  int t2 = Convert.ToInt32(s2,16);


array[0] = (byte)t1;
  array[1] = (byte)t2;


string s = System.Text.Encoding.Unicode.GetString(array);


//default方式反解码为汉字
  array[0] = (byte)196;
  array[1] = (byte)207;
  s = System.Text.Encoding.Default.GetString(array);


//取字符串长度
  s = "iam方枪枪";
  int len = s.Length;//will output as 6
  byte[] sarr = System.Text.Encoding.Default.GetBytes(s);
  len = sarr.Length;//will output as 3+3*2=9


//字符串相加
  System.Text.StringBuilder sb = new System.Text.StringBuilder("");
  sb.Append("i ");
  sb.Append("am ");
  sb.Append("方枪枪");

/////////////////////////////////////////////////////////////////////


string --> byte array

byte[] data=Syste.Text.Encoding.ASCII.GetBytes(string);

string --> byte

byte data = Convert.ToByte(string);

byte[]-->string

string string = Encoding.ASCII.GetString( bytes, 0, nBytesSize );

posted @ 2006-05-08 16:55 leithon 阅读(54) 评论(0) 编辑
 
序列化:
1、定义结构
[Serializable()]
public struct structname
{
...
}
2、序列化
public static byte[] ObjectToByteA(object obj)
{
MemoryStream fs = new MemoryStream();
byte[] tmp = null;
try
{
// 序列化
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(fs, obj);
tmp = fs.ToArray();
}
catch(Exception e)
{
MessageBox.Show(e.ToString(), "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
fs.Close();
}
return tmp;
}
3、反序列化
public static object ByteAToObject(byte[] ba)
{
MemoryStream fs = new MemoryStream();
object obj = null;
try
{
// 反序列化
fs = new MemoryStream(ba);
fs.Position = 0;
BinaryFormatter formatter = new BinaryFormatter();
obj = formatter.Deserialize(fs);
}
catch(Exception e)
{
MessageBox.Show(e.ToString(), "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
fs.Close();
}
return obj;
}
4、using引用
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;


以此为基础,可以完整实现Socket通信中结构或者对象的传输
posted @ 2006-05-08 16:24 leithon 阅读(44) 评论(0) 编辑