posted @ 2011-05-13 12:31 laosu 阅读(520) 评论(1) 编辑
posted @ 2011-03-07 13:28 laosu 阅读(516) 评论(5) 编辑
posted @ 2011-06-13 11:06 laosu 阅读(317) 评论(1) 编辑
第11章 对象集合与LINQ
【摘要】:LINQ是.NET的一项重大创新,它可以非常方便地查询对象集合。本章主要讲述LINQ技术中最基础的部分--LINQ to Objects。
第1节 针对对象集合的标准查询
1、 对象筛选:
A、 筛选指从某对象集合中选出满足条件的对象,通用Where扩展方法实现。
B、 方法声明:
Public static IEnumerable<Tsource> Where<Tsource>(
this IEnumerable<T> source,Func<Tsource,bool> predicate)
C、示例:
List<FileInfo> files = ……;
IEnumerable<FileInfo> ret = files.Where<FileInfo>(
file => Path.GetFileNameWithoutExtension(file.Name).Indexof(
txtFindWhat.Text.Trim()) != -1);
D、思考:
D1、其中file不是string 类型,而是FileInfo类型。FileInfo是.NET的类,它封装了文件的基本
信息和常用操作。
D2、也可以这样调用,和上面本质是相同的。
IEnumerable<FileInfo> ret1 = Enumerable.Where<FileInfo>(files,
file => Path.GetFileNameWithoutExtension(file.Name).IndexOf(txtFindWhat.Text.Trim()) != -1);
2、 投影与数据转换:
A、 投影:指把某对象集合中对象的部分属性抽取出来进行处理。
B、 数据转换:指将某对象集合中感兴趣的对象(或抽取它的部分字段/属性)转换为另一种类型的对 象。
C、方法声明: public static IEnumerable<TResult> Select<Tsource,TResult>(
this IEnumerable<Tsource> source,Func<Tsource,TResult> selector)
D、示例:
IEnumerable<string> fileList = Directory.GetFiles("c:\\", "*.*");
IEnumerable<FileInfo> files = fileList.Select(file => new FileInfo(file)); //转换
var items = fileList.Select(file =>
{
FileInfo fileInfo = new FileInfo(file);
return new { FileName = fileInfo.Name, Size = fileInfo.Length };
});
E、思考:
E1、参数file是String类型。
E2、投影与Sql的投影是类似的,你可以进行很灵活的操作。如返回的集合元素的文件名长度仅取10个字符,可改写为 return new { FileName = fileInfo.Name.Substring(0,10), Size = fileInfo.Length };
E3、这是items是隐式变量,它的类型是IEnumerable<T>,其中T为匿名类型,即 new {string FileName,long Size}。
3、 数据排序:
A、使用扩展方法OrderBy(升序)、OrderByDescending(降序)进行排序。
B、方法声明:
Public static IOrderedEnumerable<Tsource> OderBy<Tsource,Tkey>(
This IEnumerable<Tsource> source,Func<Tsource,Tkey> keySelector)
C、示例:
Class Pet {
Public string Name { get; set;}
Public int Age { get; set; }
}
Pet[] pets = …….;
IEnumerable<Pet> query = pets.Oderby( pet => pet.Age);
D、思考:
D1、排序的属性必须可以比较大小的,若此属性引用一个对象,则要求此实现IComparable或Icomparable<T>接口。
D2、返回的类型IOrderedEnumerable<Tsource>的基类型为IEnumerable<Tsource>。
4、 数据连接:
A、 数据连接:指根据某个对象的属性将两个数据集合的对象连接起来,得到一个新集合。
B、 Public static IEnumerable<TResult> Join<Tout,TInner,Tkey,TResult>{
This IEnumerable<Touter> outer,IEnumerable<Tinner> inner,
Func<Touter,Tkey> outerKeySelector,Func<Tinner,Tkey> inner KeySelector,
Func<Touter,Tinner,TResult> resultSelector
}
C、 示例:
var result = arrA.Join( //指明连接的第一个集合对象arrA
arrB, //指明连接的第二个集合对象arrB
Aobj => Aobj.AID, //指明第一个对象集合中的对象A的AID字段作为连接字段
Bobj => Bobj.BID, //指明第二个对象集合中的对象B的BID字段作为连接字段
(Aobj, Bobj) => new //指明结果以哪种方式返回
{
Aobj.AID,
Aobj.AName,
Bobj.BName
}
);
D、思考:
D1、用扩展方法进行数据连接用起来太复杂,怎么办,用Linq好了,Linq简单好用。
D2、要看懂并运用此函数其实不难,只需懂得扩展方法、委托、泛型即可。
5、 对应于集合代数的标准查询:
.NET提供一组扩展方法来写成标准集合运算:
Union:并集
Concat:将两个集合“首尾相连”
Intersect: 交集
Distinct:删除集合中的重复值。
Reverse: 反转集合中的重复值。
Sum:对集合中的数累加求和。
Average:求平均值。
Max 与Min:找出最大值和最小值。
OfType:筛选出指定类型的对象。
思考:
A、 Oftype的用法
IEnumerable<object> stuff = new object[] { new object(), 1, 3, 5, 7, 9, Guid.NewGuid() };
IEnumerable<int> odd = stuff.OfType<int>(); //筛选出整数类型。
B、Concat 扩展方法:首尾连接后得到的集合包括重复元素。
第2节 伟大的技术创新----LINQ
1、 概念:语言集成查询(Language-Integrated Query,LINQ)是.NET3.5引入的一项重要技术。它在标准的.NET编程语言中嵌入了一个领域特定的语言,它大大地简化了数据存取工作。
2、 意义:统一了数据存取技术,可以使用一致的方式存取多种数据源,如关系型数据、XML数据。这样不需要人工编写针对特定数据源的专用命令,而由底层的LINQ自动地进行转换。
3、 常用的Linq技术有:linq to Object、linq to DataSet、linq to SQL、linq to Entities、Linq to XML等。
4、 特性:延迟执行。
查询变量本身只是存储查询命令,实际的查询执行会延迟到foreeach语句中循环访问查询变量时发生,此即LINQ的延迟执行特性。当然,若想立即执行,可以调用IEnumerable<T>类型的ToList<Tsource>或ToArray<Tsource>扩展方法。
5、 初探LINQ to Object技术内幕
CLR并不能直接处理LINQ查询表达式,而是由C#编译器将其转化为相应的扩展方法和预定义委托。
6、 思考:
A、 何时用LINQ,何时用扩展方法?
答:前者优点:简单、方便。后者优点:灵活、强大; 对于复杂功能使用扩展方法来实现,对于简单的查询使用LINQ实现较好,当然也可混用。
第3节 掌握LINQ查询表达式的编写技巧
1、 筛选数据:
A、任务:从C盘上查找创建日期在今天以前的纯文本(即.TXT)文件。
Ienumerabled<FileInfo> files =
from fileName in Directory.GetFiles(“C:\\”)
Where File.GetLastWriteTime(fileName) < DateTime.Now.Date
&& Path.GetExtension(fileName).ToUpper() == “.TXT”
Select new FileInfo(fileName)
B、 思考:
B1、试想若不用LINQ会是多么麻烦呀!可见LINQ真是好东西。
B2、可以在select 子句中转换数据类型,动态创建新的对象。
2、 数据排序:
A、 查找C盘根目录下所有的文件,并按文件大小升序排序。
IEnumerable<string> fileName =
From fileName in Directory.GetFiles(“C:\\”)
Ordeby (new FileInfo(fileName)).Length,fileName ascending
Select fileName;
B、 要点:
B1、默认为升序,若需要降序排列,将“ascending”改为“descending”。
B2、文件长度是通过将字符串 fileName 转为 FileInfo对象,然后得到其长度。
B3、编译时将Orderby子句转换为对IEnumerable<T>.OrderBy扩展方法的调用。
3、 投影:
Var files = from fileName in Directory.GetFiles(“C:\\”)
Select new {
Name = fileName,
LastWriteTime = File.GetLastWriteTime(fileName)
}
4、 调用本地方法:
Private static bool IsEven(int num) //判断是否是偶数
{
If(num % 2)
Return true;
Return false;
}
Int[] numbers = {5,4,1,3,9,8,6,7,2,0};
Var queryEvenNums =
From num in numbers
Where IsEven(num)
Select num;
要点:
A、 由于LINQ直接与编程语言集成,因此可以在LINQ表达式中直接调用“本地方法”。
5、 嵌套使用LINQ子句:
A、 任务:在5个学生的4门课成绩单中查找至少有一门课成绩在90分以上的学生信息。
B、 代码:
Public class Student{
Public string Name {get;set;}
Public List<int> Scores { get;set;} //各门课的成绩清单
}
List<Student> students = New List<Student>();
//向students集合中添加Student对象……
Var scoreQuery = from student in students
From score in student.Scores
Where score > 90
Select new {
Name = student.Name,
score = student.Scores.Average()
};
C、 思考:
C1、何时用嵌套LINQ子句呢?
当需要处理多层次的数据时。这时用多个from子句实现。
C2、C#编译器将多层嵌套子句的LINQ 查询转换为IEnumerable<T >.SelectMany扩展方法的调用。
6、 引用新的范围变量暂存查询结果:
IEnumerable<FileInfo> files =
From fileName in Director.GetFiles(“C:\\”)
Let file = new FileInfo(fileName)
Orderby file.Length,fileName
Select file;
注:引入file范围变量暂存结果,这样可以在后面的子句中直接使用此中间变量。
第4节 对象集合的分组与连接
1、对象集合分组引例:
Public class Student
{
Public string Name {get;set;}
Public string City {get;set;}
}
Var studentQuery = from student in students
Group student by student.City;
Foreach( var studentGroup in studentQuery) //遍历每个分组
{
Console.WriteLine(“在{0}的学生清单:”,studentGroup.Key); //输出分组标识
//输出分组中的各个数据对象
Foreach(Student stu in studentGroup)
Console.WriteLine(“{0}{1}”,stu.Name,stu.City);
注:
A、 studentGroup实现了Igrouping<string,Student>接口
B、 var 真实的类型是 IEnumerable<Igrouping<string,Student>>
2、多段分组:
Public static string GroupKey(List<int> Scores)
{
Int avg = (int) Scores.Average();
String ret = “”;
Switch (avg / 10){
Case 10:
Case 9:
Ret = “优”; break;
Case 8:
Ret = “良”; break;
Case 7:
Ret = “中”; break;
Case 6:
Ret = “及格”; break;
default:
Ret = “不及格”; break;
}
return ret;
}
Var booleanGroupQuery = from student in students
Group student by GroupKey(student.Scores);
注:
A、 将数据分为多组,实现多段分组。
3、内连接:
Var query = from p in people //指定数据左集合为people
Join r in roles //指定数据右集合为roles
On p.IDRole equals r.ID //指定左右集合元素的匹配属性
Select new {
p.Name,
r.RoleDescription
};
4、左外连接:
Var query3 = from p in people //指定数据左集合为people
Join r in roles //指定数据右集合为roles
On p.IDRole equals r.id //指定左右集合元素的匹配属性
From r in pr.DefaultIfEmpty(
//如果pr为空,则创建一个IEnumerable<Role>返回
//如果pr不为空,直接返回pr
New Role {ID=3,RoleDescription = “临时工”})
Select new { p.Name,r.RoleDescription};
注:一直苦苦思索如何实现左连接呢,终于找到解了,原来这样实现的。
posted @ 2011-05-30 10:44 laosu 阅读(1782) 评论(1) 编辑
第14章 对象间的协作与信息交换
【简介】:面向对象的程序 = 对象 + 对象之间的相互协作关系。在任何一个面向对象程序开发过程中,设计好对象之间的协作关系与信息交换方式都属于开发的核心任务,本章主要介绍了“一对一”对象之间、“一对多”对象之间的相互协作与信息交换的基本方式等。
第1节 对象间信息交换的基本方式
1、 对象协作的本质:就是对象间信息交换的问题,体现为对象之间的相互访问,即设置对象属性与调用对象方法。
2、对象间信息交换的方法:获取要访问对象的引用,设置对象属性与调用对象方法。
3、案例:主窗体向从窗体传递信息。

主窗体代码:
namespace MultiFormProgram1
{
public partial class frmMain : Form
{
public frmMain()
{
InitializeComponent();
frm = new frmOther(); //创建从窗体对象
frm.Show();//显示从窗体
}
private frmOther frm = null; //◆定义私有字段,用于引用从窗体对象,很重要哟!
private void btnSend_Click(object sender, EventArgs e)
{
frm.SetValue(txtUserInput.Text); //◆调用从窗体方法,传递参数值。
}
}
}
从窗体代码:
namespace MultiFormProgram1
{
public partial class frmOther : Form
{
public frmOther()
{
InitializeComponent();
}
//对从窗体的Label设置值
public void SetValue(string s)
{
lblInfo.Text = "老大说:" + s;
}
}
}
4、思考:
A、除了用方法参数传递信息外,还有何方式?
【答】:当然是对象的属性这种更直接的方式。
B、如果需要在主窗体与从窗体双向传递怎么办?
【答】:建立双向关联,即每个对象都拥有对方的引用。即主窗体有一个私有字段引用从窗体,从窗体也有一个私有字段引用主窗体。
C、如何有很多对象都需要相互传递信息,用此方法行不行吗?
【答】:行,但不够好。这时用委托来实现。因此,此技术最适合的场所是对象的一对一通信且只有很少的对象。
第二节 多对象协作与信息交换--广播信息
1、原理:利用委托类型的变量可以引用多个方法构成的列表(委托调用列表)。
2、案例:利用委托“广而告之”
主窗体代码:
namespace UseDelegate
{
public partial class frmMain : Form
{
public frmMain()
{
InitializeComponent();
}
private Action<int> ReceiverMethods; //定义委托变量,用于引用方法
private void btnNewForm_Click(object sender, EventArgs e)
{
NewForm();
}
private void NewForm()
{
frmOther frm = new frmOther();
ReceiverMethods += frm.ShowCounter; //挂接从窗口的方法
frm.Show();
}
private int counter = 0;//计数器
private void btnClickMe_Click(object sender, EventArgs e)
{
counter++;
if (ReceiverMethods != null)
ReceiverMethods(counter); //方法调用
}
}
}
从窗口代码:
namespace UseDelegate
{
public partial class frmOther : Form
{
public frmOther()
{
InitializeComponent();
}
public void ShowCounter(int counter) //显示消息
{
lblInfo.Text = counter.ToString();
}
}
}
3、思考:
A、利于事件是否也能实现“广播消息”?
【答】:可以,因为事件的本质就是多路委托。思路:主窗体定义一个事件,从窗体对象响应之。
B、能否通知的是不同类型的对象?
【答】:可以,只要不同类型的对象的实例方法符合委托所确定的方法签名即可。
C、它实现的原理是什么?
【答】:充分利用委托的"多路"特性,实现了消息的传播(具体是通过方法参数传递的)。
第三节 多对象协作与信息交换--一个对象“监控”多个对象
1、本节介绍的是“反方向”的信息传播问题,即多个对象向一个对象发送信息,可形象地比喻成:一个对象监控多个对象。
2、案例:从窗体向主窗体传递信息:
主窗口代码:
namespace ButtonCounterForMultiFormUseDelegate
{
public partial class frmMain : Form
{
public frmMain()
{
InitializeComponent();
}
private int counter = 0;
/// <summary>
/// 显示累计按钮单击次数的结果
/// </summary>
private void ShowCounter()
{
counter++;
lblInfo.Text = counter.ToString();
}
private void btnShowOtherForm_Click(object sender, EventArgs e)
{
frmOther frm = new frmOther();
//◆◆将主窗体的方法“挂接”到从窗体对象上
frm.CallBackMethod = this.ShowCounter;
//在屏幕上显示从窗体
frm.Show();
}
}
}
从窗体代码:
namespace ButtonCounterForMultiFormUseDelegate
{
public partial class frmOther : Form
{
public frmOther()
{
InitializeComponent();
}
public Action CallBackMethod = null; //◆定义委托变量,用于引用方法
private void btnClickMe_Click(object sender, EventArgs e)
{
//回调主窗体的方法,显示按钮计数
if (CallBackMethod != null)
CallBackMethod(); //◆回调主窗体的方法
}
}
}
3、思考:
A、为什么在从窗体中定义委托变量,而不是在主窗体中定义?
【答】:因为实际需求是:当点击从窗体的按钮时,回调主窗体的方法来显示次数。
B、能否可以用事件机制实现?
【答】:可以。
C、除了利用委托或事件实现外,有无别的方法?
【答】:有的,第一种就是利用组合关系,即让从窗体对象持有主窗体对象的引用,即第一节介绍的方法。
第二种方式从窗体利用静态方法主动通知主窗体,这种方法本质上是定义了“全局”的字段与方法。
用委托实现,可以不需要相互执有对方的引用,从而减少了对象之间的耦合度。
posted @ 2011-05-25 17:37 laosu 阅读(1076) 评论(2) 编辑
第13章 对象的复制与序列化
【简介】:序列化技术是学习分布式软件系统开发技术(如WCF)的前提之一,本章主要介绍对象的浅复制与深复制、对象的序列化、对象序列化应用等。
第一节 对象的浅复制与深复制
1、 浅复制:是指当对象的字段值被复制时,字段引用的对象不会被复制。
例如,如果一个对象有一个指向字符串的字段,并且我们对该对象做了一个浅复制,那麽两个对象将引用同一个字符串。
2、 深复制:是对对象事例中字段引用的对象也进行复制的一种方式。
例如,如果一个对象有一个指向字符串的字段,并且对该对象做了一个深复制的话.我门将创建一个新的对象和一个新的字符串.新对象将指向新字符串。
3、.NET中默认的对象复制方式为浅复制,我们可以通过两种方式实现深复制。
A、类实现Icloneable接口。
B、对象序列化技术。
第二节 对象序列化
对象序列化主要解决对象状态的保存问题,而对象状态指某一时刻对象所拥有的各个字段的集合。
1、 定义:将一个内存中的对象保存到流中,并在需要时从流中重新读取数据重建对象的过程称为对象的序列化与反序列化。
用于保存对象序列化的存储介质是流(Stream),它代表一连串有顺序的二进制数据。
2、.NET基类库的流类型有:NetworkStream、FileStream、PipeStream、MemoryStream。它们的父类为Stream。
3、读写流中的数据的两组辅助类:
A、BinaryReader 和 BinaryWriter
B、StreamReader 和 StreamWriter
4、序列化有两种形式:二进制序列化与XML序列化。
前者指将对象的数据以二进制形式直接写入流中,后者指用XML方式写入流中。其中XML序列化速度较慢、生成的数据量较大、安全性不如二进制序列化。
5、BinaryFormatter类进行二进制序列化,SoapFormatter类进行XML序列化。
6、.NET中提供标记可控制序列化(或反序列化)过程。
A、[OnSerializedAttribute]:在序列化完成后调用。
B、[OnSerializingAttribute]:在序列化进行时调用。
C、[OnDeserializedAttribute]:在反序列化完成后调用。
D、[OnDeserializingAttribute]:在反序列化进行时调用。
E、[NonSerialized]:指字段不参与序列化。
第三节 对象序列化技术应用实例
1、可大批量地复制对象。
要点:不将其序列化到文件中,而将其序列化到内存流中。此完成的是“深复制”。
2、程序退出时保存状态。
3、WPF对象的XAML序列化。
XAML代码可看成是WPF对象序列化后的结果。
4、ASP.NET视图状态剖析:
正是借助序列化技术,解决了asp.net中对象(控件)状态保存的问题。
posted @ 2011-05-24 14:16 laosu 阅读(1354) 评论(0) 编辑
第10章 异步编程模式
【摘要】:异步编程充分利用多线程技术带来的好处,而不需要程序员了解多线程开发中的复杂细节。本章讲述了基于IAsyncResult的异步模式、基于事件的异步模式。
第1节 程序的同步执行和异步执行
1、同步概念:
若在代码中调用了一个方法,需要等待此方法所有的代码执行完毕之后,才能回到原来的地方执行下一行代码,这种程序运行方式称为同步。
2、异步概念:
在调用方法之后,不用等待方法执行完成就马上执行下一条语句,这种程序运行方式称为异步。
3、异步调用的本质:不在主线程中执行,而在另一辅助线程中与主线程并行执行。
第2节 基于委托的异步编程模式
1、 当定义一个委托时,编译器会生成如下的类,借助委托的BeginInvoke和EndInvoke方法实现异步调用。
Public sealed class CalculateFolderSizeDelegate:MulticastDelegate
{
Public CalculateFolderSizeDelegate(object target,int methodPtr)
{ ……}
Public virtual <方法返回值类型> Invoke (<输入和输出参数>)
{ ……}
Public virtual IAsyncResult BeginInvoke(<输入和输出变量>,AsyncCallback callback,object asyncState)
{ ……}
Public virtual <方法返回值类型> EndInvoke(<声明为ref或out的参数>,IAsyncResult result)
{ ……}
A、 BeginInvoke的第一个参数为方法签名中的参数列表,第二个参数callback是当异步调用结束时自动回调的方法,第三个参数asyncState用于向第二个参数所确定的callback回调方法提供额外的信息。
B、 BeginInvoke方法返回一个IAsyncResult接口的对象。
Public interface IAsyncResult
{
Object AsyncState {get;}
WaitHandle AsyncWaitHandle { get;}
Bool CompletedSynchronously {get;}
Bool IsCompleted {get;}
}
C、 EndInvoke:发现异步调用完成时,它取出此异步调用方法执行的结果作为其返回值,如果异步调用方法有声明为ref和out的参数,它也负责填充它。
第3节 基于委托的异步编程模式的实践
1、 当异步调用完成之时,调用者线程如何知道调用的执行情况。
A、 使用轮询 如通过IAsyncResult对象的IsCompleted或AsyncWaitHandle实现。
B、 异步回调。
2、 处理异常调用中的异常。
需在EndInvoke方法所在的代码处捕获异步调用抛出的异常。
3、实现异步调用任务的同步问题。
第4节 实现IAsyncResult异步调用模式的组件
1、 在.NET基类型库中,有一些现有的组件直接实现了IAsyncResult异步调步模式,这些组件通常同时提供某个方法的同步与异步调用形式。
以WebRequest 为例。
IAsyncResult BeginGetResponse(AsyncCallback callback, object state) //异步
IAsyncResult BeginGetRequestStream(AsyncCallback callback, object state) //异步
Stream EndGetRequestStream(IAsyncResult asyncResult)
WebResponse EndGetResponse(IAsyncResult asyncResult)
Stream GetRequestStream() //同步
WebResponse GetResponse() //同步
2、 方法有如下特点:
A、凡有一个BeginXXX的,一定有一个“EndXXX”方法相对应。
B、每组“BeginXXX/EndXXX”,一定有一个对应的“XXX”同步方法。
C、“EndXXX”方法与对应的同步方法“XXX”的返回值类型相同。
D、“BeginXXX”返回一个IAsyncResult对象,而“EndXXX”方法的参数接收此对象。
3、此模式前基于委托的异步调用模式几乎一模一样,因此前面介绍的编程技巧可以继续使用。
第5节 基于事件的异步调用模式
1、 在.NET基类库中有部分组件实现了另一种异步模式,这就是基于事件的异步模式(Event-based Asynchronous Pattern),简称为EAP。
以WebClient 为例。
public void DownloadFileAsync(Uri address, string fileName);
public void DownloadFileAsync(Uri address, string fileName,object userToken);
public void CancelAsync(); //取消任务
2、 EAP有如下特点:
A、 实现了EAP的组件定义了以“Async”结尾的异步调用方法。
B、 当异步调用任务结束时,会激发一个相应的事件,事件的参数包含重要的信息。
如 WebClient会激发DownloadFileCompleted事件,参数信息存在 AsyncCompletedEventArgs对象中。
C、 实现了EAP的组件可能会提供一个用于取消异步任务的方法。
D、实现了EAP的组件提供一个向用户报告进度的事件。
如WebClient的 DownloadProgressChanged 事件。
3、【感】:用EAP组件原来是这么简单。
第6节 异步编程小结
1、 分类:异步编程分为 基于IAsyncResult的异步模式和基于事件的异步模式。而前者又分为基于委托的异步调用与使用类基类库中的组件两种。
2、 异步调用的每个方法都是独立的线程中执行的,其本质上就是一种多线程程序。
3、 适合场景:
A、 直接使用基类库中拥有异步调用特性的组件,如WebClient。
B、 需要在后台运行一些较耗费时间的任务,这些任务彼此相互独立,而且没有代码直接访问可视化的控件。
4、 不适合场景:
当在后台完成的工作有复杂的同步关系,或者必须访问共享的资源,不适合采用异步编程方法,而应直接应用多线程技 术或任务并行库来开发。
posted @ 2011-05-23 17:02 laosu 阅读(1519) 评论(0) 编辑
posted @ 2011-05-19 11:25 laosu 阅读(1793) 评论(0) 编辑
posted @ 2011-05-13 12:31 laosu 阅读(520) 评论(1) 编辑
posted @ 2011-05-13 12:24 laosu 阅读(1798) 评论(4) 编辑
posted @ 2011-05-03 18:33 laosu 阅读(1157) 评论(0) 编辑
posted @ 2011-04-29 09:59 laosu 阅读(1420) 评论(3) 编辑
