路径,文件,目录,I/O常见操作汇总(转)
posted @ 2007-07-02 14:48 星空下的悠云 阅读(104) 评论(0) 编辑
2007年7月2日 #
posted @ 2007-07-02 14:48 星空下的悠云 阅读(104) 评论(0) 编辑
2006年7月5日 #
posted @ 2006-07-05 14:16 星空下的悠云 阅读(158) 评论(0) 编辑
2006年6月29日 #
posted @ 2006-06-29 14:27 星空下的悠云 阅读(232) 评论(0) 编辑
2006年6月27日 #
前序:
private void PreTraWithoutRecurtion(TreeNode tn, ListView lv)
{
Stack stack = new Stack();
while(tn.Nodes.Count != 0)
{
lv.Items.Add(tn.Text);
stack.Push(tn);
tn = tn.Nodes[0];
}
lv.Items.Add(tn.Text);
while(stack.Count != 0)
{
tn = (TreeNode)stack.Pop();
if(tn.Nodes.Count > 1)
{
tn = tn.Nodes[1];
while(tn.Nodes.Count != 0)
{
lv.Items.Add(tn.Text);
stack.Push(tn);
tn = tn.Nodes[0];
}
lv.Items.Add(tn.Text);
}
}
}
中序:
private void InTraWithoutRecursion(TreeNode tn, ListView lv)
{
Stack stack = new Stack();
while(tn.Nodes.Count != 0)
{
stack.Push(tn);
tn = tn.Nodes[0];
}
lv.Items.Add(tn.Text);
while(stack.Count != 0)
{
tn = (TreeNode)stack.Pop();
lv.Items.Add(tn.Text);
if(tn.Nodes.Count > 1)
{
tn = tn.Nodes[1];
while(tn.Nodes.Count != 0)
{
stack.Push(tn);
tn = tn.Nodes[0];
}
lv.Items.Add(tn.Text);
}
else if(stack.Count != 0)
{
tn = (TreeNode)stack.Pop();
lv.Items.Add(tn.Text);
if(tn.Nodes.Count >1)
{
tn = tn.Nodes[1];
while(tn.Nodes.Count != 0)
{
stack.Push(tn);
tn = tn.Nodes[0];
}
lv.Items.Add(tn.Text);
}
}
}
}
后序待续。。。
后序来了:
其实感觉所谓程序就是解决问题思路的“再现”,怎么想的怎么写基本上就差不多了,所以关键怎么想...
always thinking....
/// <summary>
/// 非递归后序遍历二叉树
/// 注:在语句上方的注释为其下面的程序块的说明
/// 在语句后面的为该语句的说明
/// =Color.Red 表示从右进入
/// =Color.Lavender表示从左进入
/// ==Color.Red 表示从右回来
/// </summary>
/// <param name="tn">当前子树的根</param>
/// <param name="lv">保存结果的列表</param>
private void PostTraWithoutRecursion(TreeNode tn, ListView lv)
{
Stack stack = new Stack();
//找到最左下节点
while(tn.Nodes.Count != 0)
{
tn.BackColor = Color.Lavender;
stack.Push(tn);
tn = tn.Nodes[0];
}
tn.BackColor = Color.Red;
lv.Items.Add(tn.Text);
tn.BackColor = Color.Empty;
//开始其他节点访问
while(true)
{
//指向栈顶节点
if(stack.Count != 0)
tn = (TreeNode)stack.Peek();
else
return;
//访问从右子节点回来的点
while(tn.BackColor == Color.Red)
{
lv.Items.Add(tn.Text);
tn.BackColor = Color.Empty;
stack.Pop();//访问完毕弹出
if(stack.Count != 0)//指向栈顶节点
{
tn = (TreeNode)stack.Peek();
}
else
return;
}
//从右子节点进入
tn.BackColor = Color.Red;
if(tn.Nodes.Count > 1)
{
tn = tn.Nodes[1];//指向右子节点
//找到右子节点的最左下节点
while(tn.Nodes.Count != 0)
{
tn.BackColor = Color.Lavender;
stack.Push(tn);
tn = tn.Nodes[0];
}
//访问最左下
tn.BackColor = Color.Red;
lv.Items.Add(tn.Text);
tn.BackColor = Color.Empty;
}
//没有右子节点,访问之
else
{
lv.Items.Add(tn.Text);
tn.BackColor = Color.Empty;
stack.Pop();
}
}
}
posted @ 2006-06-27 15:43 星空下的悠云 阅读(905) 评论(0) 编辑
void preorder_recursive(Bitree T) /* 先序遍历二叉树的递归算法 */
{
if (T) {
visit(T); /* 访问当前结点 */
preorder_recursive(T->;lchild); /* 访问左子树 */
preorder_recursive(T->;rchild); /* 访问右子树 */
}
}
void preorder_nonrecursive(Bitree T) /* 先序遍历二叉树的非递归算法 */
{
initstack(S);
push(S,T); /* 根指针进栈 */
while(!stackempty(S)) {
while(gettop(S,p)&&p) { /* 向左走到尽头 */
visit(p); /* 每向前走一步都访问当前结点 */
push(S,p->;lchild);
}
pop(S,p);
if(!stackempty(S)) { /* 向右走一步 */
pop(S,p);
push(S,p->;rchild);
}
}
}
void inorder_recursive(Bitree T) /* 中序遍历二叉树的递归算法 */
{
if (T) {
inorder_recursive(T->;lchild); /* 访问左子树 */
visit(T); /* 访问当前结点 */
inorder_recursive(T->;rchild); /* 访问右子树 */
}
}
void inorder_nonrecursive(Bitree T)
{
initstack(S); /* 初始化栈 */
push(S, T); /* 根指针入栈 */
while (!stackempty(S)) {
while (gettop(S, p) && p) /* 向左走到尽头 */
push(S, p->;lchild);
pop(S, p); /* 空指针退栈 */
if (!stackempty(S)) {
pop(S, p);
visit(p); /* 访问当前结点 */
push(S, p->;rchild); /* 向右走一步 */
}
}
}
void postorder_recursive(Bitree T) /* 中序遍历二叉树的递归算法 */
{
if (T) {
postorder_recursive(T->;lchild); /* 访问左子树 */
postorder_recursive(T->;rchild); /* 访问右子树 */
visit(T); /* 访问当前结点 */
}
}
typedef struct {
BTNode* ptr;
enum {0,1,2} mark;
} PMType; /* 有mark域的结点指针类型 */
void postorder_nonrecursive(BiTree T) /* 后续遍历二叉树的非递归算法 */
{
PMType a;
initstack(S); /* S的元素为PMType类型 */
push (S,{T,0}); /* 根结点入栈 */
while(!stackempty(S)) {
pop(S,a);
switch(a.mark)
{
case 0:
push(S,{a.ptr,1}); /* 修改mark域 */
if(a.ptr->;lchild)
push(S,{a.ptr->;lchild,0}); /* 访问左子树 */
break;
case 1:
push(S,{a.ptr,2}); /* 修改mark域 */
if(a.ptr->;rchild)
push(S,{a.ptr->;rchild,0}); /* 访问右子树 */
break;
case 2:
visit(a.ptr); /* 访问结点 */
}
}
}
void preorder_recursive(Bitree T) /* 先序遍历二叉树的递归算法 */
{
if (T) {
visit(T); /* 访问当前结点 */
preorder_recursive(T->;lchild); /* 访问左子树 */
preorder_recursive(T->;rchild); /* 访问右子树 */
}
}
f(n) = n + 1; (n <2)
f[n/2] + f[n/4](n >;= 2);
这个例子相对简单一些,递归程序如下:
int f_recursive(int n)
{
int u1, u2, f;
if (n < 2)
f = n + 1;
else {
u1 = f_recursive((int)(n/2));
u2 = f_recursive((int)(n/4));
f = u1 * u2;
}
return f;
}
int f_nonrecursive(int n)
{
int stack[20], flag[20], cp;
/* 初始化栈和栈顶指针 */
cp = 0;
stack[0] = n;
flag[0] = 0;
while (cp >;= 0) {
switch(flag[cp]) {
case 0: /* 访问的是根结点 */
if (stack[cp] >;= 2) { /* 左子树入栈 */
flag[cp] = 1; /* 修改标志域 */
cp++;
stack[cp] = (int)(stack[cp - 1] / 2);
flag[cp] = 0;
} else { /* 否则为叶子结点 */
stack[cp] += 1;
flag[cp] = 2;
}
break;
case 1: /* 访问的是左子树 */
if (stack[cp] >;= 2) { /* 右子树入栈 */
flag[cp] = 2; /* 修改标志域 */
cp += 2;
stack[cp] = (int)(stack[cp - 2] / 4);
flag[cp] = 1;
} else { /* 否则为叶子结点 */
stack[cp] += 1;
flag[cp] = 2;
}
break;
case 2: /* */
if (flag[cp - 1] == 2) { /* 当前是右子树吗? */
/*
* 如果是右子树, 那么对某一棵子树的后序遍历已经
* 结束,接下来就是对这棵子树的根结点的访问
*/
stack[cp - 2] = stack[cp] * stack[cp - 1];
flag[cp - 2] = 2;
cp = cp - 2;
} else
/* 否则退回到后序遍历的上一个结点 */
cp--;
break;
}
}
return stack[0];
}
void swap(int array[], int low, int high)
{
int temp;
temp = array[low];
array[low] = array[high];
array[high] = temp;
}
int partition(int array[], int low, int high)
{
int p;
p = array[low];
while (low < high) {
while (low < high && array[high] >;= p)
high--;
swap(array,low,high);
while (low < high && array[low] <= p)
low++;
swap(array,low,high);
}
return low;
}
void qsort_recursive(int array[], int low, int high)
{
int p;
if(low < high) {
p = partition(array, low, high);
qsort_recursive(array, low, p - 1);
qsort_recursive(array, p + 1, high);
}
}
void qsort_nonrecursive(int array[], int low, int high)
{
int m[50], n[50], cp, p;
/* 初始化栈和栈顶指针 */
cp = 0;
m[0] = low;
n[0] = high;
while (m[cp] < n[cp]) {
while (m[cp] < n[cp]) { /* 向左走到尽头 */
p = partition(array, m[cp], n[cp]); /* 对当前结点的访问 */
cp++;
m[cp] = m[cp - 1];
n[cp] = p - 1;
}
/* 向右走一步 */
m[cp + 1] = n[cp] + 2;
n[cp + 1] = n[cp - 1];
cp++;
}
}
akm(m, n) = n + 1; (m = 0时)
akm(m - 1, 1); (n = 0时)
akm(m - 1, akm(m, n - 1)); (m != 0且n != 0时)
int akm_recursive(int m, int n)
{
int temp;
if (m == 0)
return (n + 1);
else if (n == 0)
return akm_recursive(m - 1, 1);
else {
temp = akm_recursive(m, n - 1);
return akm_recursive(m - 1, temp);
}
}
int akm_nonrecursive(int m, int n)
{
int m1[50], n1[50], cp;
cp = 0;
m1[0] = m;
n1[0] = n;
do {
while (m1[cp] >; 0) { /* 压栈, 直到m1[cp] = 0 */
while (n1[cp] >; 0) { /* 压栈, 直到n1[cp] = 0 */
cp++;
m1[cp] = m1[cp - 1];
n1[cp] = n1[cp - 1] - 1;
}
/* 计算akm(m - 1, 1),当n = 0时 */
m1[cp] = m1[cp] - 1;
n1[cp] = 1;
}
/* 改栈顶为akm(m - 1, n + 1),当m = 0时 */
cp--;
m1[cp] = m1[cp] - 1;
n1[cp] = n1[cp + 1] + 1;
} while (cp >; 0 || m1[cp] >; 0);
return n1[0] + 1;
}
g(m, n) = 0 (m = 0, n >;= 0)
= g(m - 1, 2n) + n; (m >; 0, n >;= 0)
int g_recursive(int m, int n)
{
if (m == 0 && n >;= 0)
return 0;
return (g_recurse(m - 1, 2*n) + n);
}
int g_nonrecursive(int m, int n)
{
int p;
for (p = 0; m >; 0 && n >;= 0; m--, n *= 2)
p += n;
return p;
}
f(n) = n + 1 (n = 0)
n * f(n/2) (n >; 0)
int f_recursive(int n)
{
if (n == 0)
return 1;
return (n * f_recurse(n/2));
}
int f_nonrecursive(int n)
{
int m;
for (m = 1; n >; 0; n /= 2)
m *= n;
return m++;
}
分析完了递归程序的分类,让我们回头看看在向非递归转换的过程中用到了什么来实现转换:
1)循环,因为程序要在某个条件下一直执行下去,要代替递归程序,循环必不可少,对于尾部递归,循环结束的
条件十分容易确定,只要按照不同分支的条件写出来就可以了.而对于非尾部递归程序,循环结束的条件一
般是当栈为空时或者是结束了对递归调用树的遍历从树的根结点退出时,而且有的时候写成while()的形式
,有时写成do ...while的形式(如上面的akm函数),具体怎样,很难说清楚,取决于你对整个递归程序的分析
.
2)递归调用树,树的结构在转换的过程中是不可见的,你不必为转换专门写一个树结构,不过能不能把递归调用
中的树遍历方式以及叶子结点,左子树,右子树等元素确定好是你能否正确解决问题的关键(这一点已经在
上面的分析过程中展露无疑),确定好这些后,剩下的工作大部分就是按照给出的几种不同的遍历树的方式
把程序进行改写,这个过程就考验你对树结构还有遍历方式是否很好的掌握了(看出基础的重要了吗?如果
回答是,那么和我一样好好的打好基础吧,一切都还不晚!!).对于尾部递归而言,可以看作没有递归调用树,
所以尾部递归的难度大大降低了.
3)栈,非尾部调用中需要栈来保存数据,这一点已经很清楚了,需要注意几个问题:a)栈有时可能会出现不够的
情况,拿上面的akm函数来说,我用的50个元素的数组,你如果把m和n值设置得大一些,这个栈就不能用了,有
时你的算法正确了,不过没有注意到这个问题还是会出错的;反过来说,在递归调用中,系统或者编译器的优
化功能不够好的化,在这个栈上可能会消耗很多空间,这个时候如果你把程序改成非递归的形式,然后再用
动态分配技术分配栈可能就会把程序的性能提高一大块--这也是我们学习这门技术的意义之一,因为系统
是机械化的,你如果知道更好的优化办法,为什么不用呢?
什么时候可以用递归解决问题?到了这一步,如果你对于上面说的已经相当明白的话,这个问题不难回答,如果
我们要解决的问题要分成几个小的部分,而其中的一些与你要解决的问题是一样的,只不过是问题的规模(如
参数等)小了,那么这样的问题可以用递归来解决.根据问题设计好一个递归是所有这些的基础,转换也是在原
来的递归程序上进行的,所以这一步一定要做好.通常,设计一个递归程序要注意一下几个问题:a)可以递归解
决的问题是什么?b)入口和出口参数是什么--即要明确好出入的接口.
四.学习过程中参考到的一些资料
1)<<算法导论>;>; 国防科大出版社 张益新 沈雁编著
这本书比较老了,也许很难找了,不过参考价值不大,里面专门用了一章来讲这个问题,可惜用的是goto来实
现递归调用的选择分支(所以我说参考价值不大),如果没有也无所谓的.
2)http://db.pku.edu.cn/mzhang/ 北京大学张铭老师的主页.
上面可以找到一个张老师讲解递归和非递归转换的视频,有大概50多分钟吧,我没有听完,原先看第一本书
的对于如何不用goto实现选择分支十分不解,张老师讲到的递归调用树真正让我豁然开朗,从此以后虽然还
是有很多问题,不过明白了这个大体的解决思路就有了.
3)<<数据结构>;>; 清华大学出版社 严蔚敏著
这本书讲到栈的时候略微讲到了一些这方面的内容,网上可以找到一位叫一具的仁兄写的这本书的习题解答
,可以自己去找一找,我的中序和后序遍历的算法完全看的他写的.
4) http://www.tztvu.edu.cn/kfjy/bkjy/jsj/bxk/sjjg/kcdh/忘了是什么的网页了
里面的一个dn5.doc的文件中有讲解转换akm函数的方法,给我的启发很大,如果你对于我说的akm函数还有疑
问,那么可以看看人家写的,他把递归调用树和栈的变化情况都写出来了,非常直观.
原文链接:http://bbs.chinaunix.net/viewthread.php?tid=331522
转载请注明作者名及原文出处
posted @ 2006-06-27 15:27 星空下的悠云 阅读(791) 评论(0) 编辑
posted @ 2006-06-27 13:46 星空下的悠云 阅读(194) 评论(2) 编辑
2006年6月26日 #
Serialization的概念
Serialization是.NET中一种实现对象持久性(Persistent)的机制。它是一个将对象中的数据转换成一个单一元素(通常是Stream)的过程。它的逆过程是Deserialization。Serialization的核心概念是将一个对象的所有数据看作一个独立的单元。
一般说来,在两种情况下非常需要Serialization:1)当我们希望能够将对象当前的状态完整地保存到存储介质中,以便我们以后能够精确地还原对象时;2)当我们希望将对象从一个应用程序空间(Application domain)传递到另一个应用程序空间时。例如,Windows Form程序就是利用Serialization机制来实现剪贴板的copy & paste的。
.NET Framework支持两种类型的Serialization:Shallow Serialization和Deep Serialization。
所谓Shallow Serialization是将对象的可读写(read-write)属性的值转换成字节流,而对象内部的数据(没有通过read-write属性暴露出来的数据)则不被转换。XmlSerializer以及Web Services就使用这种技术。
Deep Serialization比Shallow Serialization更加彻底,因为它是将存储在对象私有变量里的实际值拷贝到字节流里。而且Deep Serialization还将serialize整个object graph。也就是说,如果你的对象持有其他对象的引用,或者其他对象引用的集合,那么所有这些对象都将被Serialize。BinaryFormatter和SoapFormatter以及.NET Remoting都使用Deep Serialization技术,它甚至被有限地用于LosFormatter来产生存储在Web Form页中的状态数据。
本文将着重于Deep Serialization。
Serialization的过程
.NET Framework通过Reflection提供自动Serialization的机制。当一个对象被序列化(Serialized)的时候,它的类名,Assembly,以及类实例的所有数据成员都将被写入存储介质中。Serialization引擎保持对所有已经被序列化的对象引用的追踪,以确保相同的对象引用最多只被序列化一次。
通常,一个Serialization过程会由formatter(例如BinaryFormatter)的Serialize方法引发。对象的Serialization过程按照以下规则进行:
1、 检测以确保formatter是否拥有一个代理选择器(surrogate selector)。如果有,检查代理选择器是否持有给定的对象类型。如果有,ISerializable.GetObjectData被调用。
2、 如果formatter没有代理选择器,或者代理选择器没有对象类型,检查对象是否被用Serializable属性标记。如果没有,则抛出SerializationException异常。
3、 如果对象被标记为Serializable,检查对象是否实现了ISerializable接口。如果实现了此接口,则GetObjectData被调用。
4、 如果对象没有实现ISerializable接口,则使用默认的序列化策略,来序列化没有用NonSerialized属性标记的域。
使你的class能够被序列化
通过上面对Serialization过程的分析,我们可以看出,有两种方式可以使一个class能够被序列化:1)将此class简单地标记为Serializable;2)为此class实现ISerializable接口,并将此class标记为Serializable。
1、 标记Serializable属性
标记Serializable属性的方式是实现Serialization的基本方法。举个简单的例子:
[Serializable]
public class Person
{
public string name = null;
public int age = 0;
}
你可以使用BinaryFormatter来将上面的class序列化:
Person sam = new Person();
sam.name = "sam";
sam.age = 24;
IFormatter formatter = new BinaryFormatter();
Stream stream = new FileStream("sam.dat",
FileMode.Create, FileAccess.Write, FileShare.None);
formatter.Serialize(stream, sam);
stream.Close();
就是这么简单,你所要做的就是创建一个Stream和一个formatter的实例,然后调用formatter的Serialize方法。经过BinaryFormatter serialize的数据仍然能够通过BinaryFormatter deserialize回来,方法与serialize同样简单,这里就不赘述了。
如果你不想将类里的所有域都序列化,可以使用NonSerialized属性进行选择。如:
[Serializable]
public class Person
{
public string name = null;
[NonSerialized]
public int age = 0;
}
这样,age域就不会被序列化了。
需要注意的是,Serializable属性并不能被继承。也就是说如果你希望Person的派生类也能够被Serialize的话,那么这个派生类也必须被Serializable标记。否则将得到SerializationException异常。
同样的,Person类中的所有对其他类的引用,其所引用的类都应该是能够被Serialize的。.NET Framework中的大部分class都实现了ISerializable接口,但有些class没有实现,例如ImageList。可以通过MSDN Library的到一个实现了ISerializable接口的class列表。对那些没有实现此接口的class,使用的时候要当心。
2、 实现ISerializable接口
Serializable属性的功能非常强大,它使得Serialize和Deserialize变得十分简单。但凡事有利必有弊,由Serializable实现的自动序列化方法有时不够灵活。我们并不能完全控制Serialize和Deserialize的行为,而有些时候它们的行为对我们来说很重要。那么我们通过何种方法能够控制Serialize和Deserialize的行为呢?答案就是,自己来实现ISerializable接口。ISerializable接口给予我们更大的自由来控制Serialize和Deserialize,但是无疑我们将不得不写更多的代码L。
下面我们来看看如何实现ISerializabe接口。ISerializable接口位于System.Runtime.Serialization名字空间中,声明如下:
public inferface ISerializable
{
void GetObjectData(SerializationInfo info,
StreamingContext context);
}
它只有一个方法GetObjectData。因此,像实现其他接口一样,我们必须实现此方法。但与其他接口不同的是,为了Deserialization,我们还必须实现一个特殊的构造函数(我称此构造函数为“序列化构造函数”),此构造函数具有与GetObjectData相同的参数列表。由于此构造函数专门用于.NET Framework在Deserialize时的Reflection机制,因此我们通常将它声明为保护或私有模式。如下:(当然,如果你的class只需要Serialize而不需要Deserialize的话,也可以不实现这个特殊的构造函数)
[Serializable]
public class Person : ISerializable
{
public string name = null;
public int age = 0;
public Person()
{
}
protected Person(SerializationInfo info, StreamingContext context)
{
name = info.GetString("name");
age = info.GetInt32("age");
}
void ISerializable.GetObjectData(SerializationInfo info,
StreamingContext context)
{
info.AddValue("name", name);
info.AddValue("age", age);
}
}
通过实现ISerializable接口,使得我们有机会在ISerializable.GetObjectData中控制Serialize的行为,在“序列化构造函数”中控制Deserialize的行为。这个接口提供给我们的信息非常全面而灵活,以致于我们甚至可以在这两个方法中耍些花招。比如,我们可以在Deserialize的时候,籍由改变info.FullTypeName来得到一种与被Serialize的对象不同类型的另一个对象等。
独辟蹊径
前面谈到过Serialization被运用的典型环境,是对象存储、进程间数据传递等涉及到对象持久性的领域。但实际上,它也能够被运用到其他的许多地方,关键在于我们是否能想到去用运Serialization,有时候思维定式也是很可怕的J。举个例子,我们来看看在Clone方法中如何使用Serialization[1]。
如果我们要为Person类实现Clone方法,我们通常会这样写:
[Serializable]
public class Person : ICloneable
{
public string name = null;
public int age = 0;
public object Clone()
{
Person person = new Person();
person.name = name;
person.age = age;
return person;
}
}
如果我们利用Serialization的方法,Clone函数就能写成下面的样子:
public object Clone()
{
MemoryStream stream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, this);
stream.Position = 0;
return formatter.Deserialize(stream);
}
从这两个实现上看,使用Serialization实现Clone方法似乎并没有什么好处。可是设想如果你面对的是一个复杂的类继承体系,从基类到派生类都需要实现Clone方法。利用第一种实作手法,你将不得不为每一个class写一个Clone方法,而且随着数据成员的增多,这个方法将越来越冗长,并且会由于数据成员的改变而引发错误(我曾经遇到过好几次,由于class中增加了成员变量,而Clone方法没有及时更新,导致运行时错误。呵,这种错误还很难调试)。现在你看到用Serialization实现的好处了吧?是的,我们只要在基类中将Clone方法声明为virtual,并用Serialization的方法实现之,然后保证基类和派生类都可以被Serialize,上面所有的麻烦不都迎刃而解了吗?
总结
现代软件项目中,无论何种项目都会或多或少地涉及到对象持久性的问题,.NET也不例外,无论是Windows Form、ASP.NET,还是Web Services,都需要处理对象持久性。而Serialization正是.NET为应对这个问题而给出的解法。
参考文献
·[1] Rockford Lhotka,《Object Serialization in Visual Basic .NET》,MSDN Library。Serialization在Clone方法中的运用即来自此文。
·Piet Obermeyer and Jonathan Hawkins,《Object Serialization in the .NET Framework》,MSDN Library。
·Jeffrey Richter,《.NET Run-time Serialization》Part 1,Part 2,Part 3,MSDN Library。
posted @ 2006-06-26 13:29 星空下的悠云 阅读(232) 评论(2) 编辑
2006年6月9日 #
posted @ 2006-06-09 17:56 星空下的悠云 阅读(173) 评论(0) 编辑
2006年6月2日 #
posted @ 2006-06-02 14:21 星空下的悠云 阅读(2398) 评论(1) 编辑
2006年5月31日 #