Tuesday, April 15, 2008

Reflection , Attributue

(1). Declare customerized attrubute class ---Class

// Define the Command Handler Custom Attribute:
[AttributeUsage( AttributeTargets.Class )]
public class CommandHandlerAttribute : Attribute
{
  public CommandHandlerAttribute( )
  {
  }
}
(2).--Property

[AttributeUsage( AttributeTargets.Property ) ]
public class DynamicMenuAttribute : System.Attribute
{
  private string _menuText;
  private string _parentText;

  public DynamicMenuAttribute( string CommandText,
    string ParentText )
  {
    _menuText = CommandText;
    _parentText = ParentText;
  }

  public string MenuText
  {
    get { return _menuText; }
    set { _menuText = value; }
  }

  public string ParentText
  {
    get { return _parentText; }
    set { _parentText = value; }
  }
}

(3). Apply for the attributes:

 

[ CommandHandler ]
public class CmdHandler
{
[DynamicMenu( "Test Command", "Parent Menu" )]
public EventHandler CmdFunc
{
get
{
if ( theCmdHandler == null )
theCmdHandler = new System.EventHandler
(this.DynamicCommandHandler);
return theCmdHandler;
}
}

private void DynamicCommandHandler(
object sender, EventArgs args )
{
// Contents elided.
}
}

 


(4) Test

// Find all the assemblies in the Add-ins directory:
string AddInsDir = string.Format( "{0}/Addins", Application.StartupPath);
string[] assemblies = Directory.GetFiles( AddInsDir, "*.dll" );
foreach ( string assemblyFile in assemblies )
{
Assembly asm = Assembly.LoadFrom( assemblyFile );
// Find and install command handlers from the assembly.
foreach( System.Type t in asm.GetExportedTypes( ))
{
 if (t.GetCustomAttributes(
typeof( CommandHandlerAttribute ), false).Length > 0 )
{
// Found a command handler type:
ConstructorInfo ci =
t.GetConstructor( new Type[0] );
if ( ci == null ) // No default ctor
continue;
object obj = ci.Invoke( null );
PropertyInfo [] pi = t.GetProperties( );

// Find the properties that are command
// handlers
foreach( PropertyInfo p in pi )
{
string menuTxt = "";
string parentTxt = "";
object [] attrs = p.GetCustomAttributes(
typeof ( DynamicMenuAttribute ), false );
foreach ( Attribute at in attrs )
{
DynamicMenuAttribute dym = at as
DynamicMenuAttribute;
if ( dym != null )
{
// This is a command handler.
menuTxt = dym.MenuText;
parentTxt = dym.ParentText;
MethodInfo mi = p.GetGetMethod();
EventHandler h = mi.Invoke( obj, null )
as EventHandler;
UpdateMenu( parentTxt, menuTxt, h );
}
}
}
}
}

private void UpdateMenu( string parentTxt, string txt,
EventHandler cmdHandler )
{
MenuItem menuItemDynamic = new MenuItem();
menuItemDynamic.Index = 0;
menuItemDynamic.Text = txt;
menuItemDynamic.Click += cmdHandler;

//Find the parent menu item.
foreach ( MenuItem parent in mainMenu.MenuItems )
{
if ( parent.Text == parentTxt )
{
parent.MenuItems.Add( menuItemDynamic );
return;
}
}
// Existing parent not found:
MenuItem newDropDown = new MenuItem();
newDropDown.Text = parentTxt;
mainMenu.MenuItems.Add( newDropDown );
newDropDown.MenuItems.Add( menuItemDynamic );
}

Friday, April 11, 2008

(ZT)C#中的delegate和event


 


在基于Windows平台的程序设计中,事件(event)是一个很重要的概念。因为在几乎所有的Windows应用程序中,都会涉及大量的异步调用,比如响应点击按钮、处理Windows系统消息等,这些异步调用都需要通过事件的方式来完成。即使在下一代开发平台——.NET中也不例外。

那么什么是事件呢?所谓事件,就是由某个对象发出的消息,这个消息标志着某个特定的行为发生了,或者某个特定的条件成立了。比如用户点击了鼠标、 socket上有数据到达等。那个触发(raise)事件的对象称为事件的发送者(event sender),捕获并响应事件的对象称为事件的接收者(event receiver)。

在这里,我们将要讨论的是,在.NET的主流开发语言C#中如何使用自定义的事件来实现我们自己的异步调用。

在C#中,事件的实现依赖于delegate,因此我们有必要先了解一下delegate的概念。

Delegate

delegate 是C#中的一种类型,它实际上是一个能够持有对某个方法的引用的类。与其它的类不同,delegate类能够拥有一个签名(signature),并且它只能持有与它的签名相匹配的方法的引用。它所实现的功能与C/C++中的函数指针十分相似。它允许你传递一个类A的方法m给另一个类B的对象,使得类B的对象能够调用这个方法m。但与函数指针相比,delegate有许多函数指针不具备的优点。首先,函数指针只能指向静态函数,而delegate既可以引用静态函数,又可以引用非静态成员函数。在引用非静态成员函数时,delegate不但保存了对此函数入口指针的引用,而且还保存了调用此函数的类实例的引用。其次,与函数指针相比,delegate是面向对象、类型安全、可靠的受控(managed)对象。也就是说,runtime能够保证 delegate指向一个有效的方法,你无须担心delegate会指向无效地址或者越界地址。

实现一个delegate是很简单的,通过以下3个步骤即可实现一个delegate:

1. 声明一个delegate对象,它应当与你想要传递的方法具有相同的参数和返回值类型。
2. 创建delegate对象,并将你想要传递的函数作为参数传入。
3. 在要实现异步调用的地方,通过上一步创建的对象来调用方法。

下面是一个简单的例子:
using System;
public class MyDelegateTest
{
// 步骤1,声明delegate对象
public delegate void MyDelegate(string name);
// 这是我们欲传递的方法,它与MyDelegate具有相同的参数和返回值类型
public static void MyDelegateFunc(string name)
{
Console.WriteLine("Hello, {0}", name);
}

public static void Main()
{
// 步骤2,创建delegate对象
MyDelegate md = new MyDelegate(MyDelegateTest.MyDelegateFunc);
// 步骤3,调用delegate
md("sam1111");
}
}
输出结果是:Hello, sam1111

了解了delegate,下面我们来看看,在C#中对事件是如何处理的。
在C#中处理事件

C#中的事件处理实际上是一种具有特殊签名的delegate,象下面这个样子:
public delegate void MyEventHandler(object sender, MyEventArgs e);
其中的两个参数,sender代表事件发送者,e是事件参数类。MyEventArgs类用来包含与事件相关的数据,所有的事件参数类都必须从 System.EventArgs类派生。当然,如果你的事件不含参数,那么可以直接用System.EventArgs类作为参数。

就是这么简单,结合delegate的实现,我们可以将自定义事件的实现归结为以下几步:
1. 定义delegate对象类型,它有两个参数,第一个参数是事件发送者对象,第二个参数是事件参数类对象。
2. 定义事件参数类,此类应当从System.EventArgs类派生。如果事件不带参数,这一步可以省略。
3. 定义事件处理方法,它应当与delegate对象具有相同的参数和返回值类型。
4. 用event关键字定义事件对象,它同时也是一个delegate对象。
5. 用+=操作符添加事件到事件队列中(-=操作符能够将事件从队列中删除)。
6. 在需要触发事件的地方用调用delegate的方式写事件触发方法。一般来说,此方法应为protected访问限制,既不能以public方式调用,但可以被子类继承。名字是OnEventName。
7. 在适当的地方调用事件触发方法触发事件。

下面是一个简单的例子:
using System;
public class EventTest
{
// 步骤1,定义delegate对象
public delegate void MyEventHandler(object sender, System.EventArgs e);
// 步骤2省略
public class MyEventCls
{
// 步骤3,定义事件处理方法,它与delegate对象具有相同的参数和返回值类型
public void MyEventFunc(object sender, System.EventArgs e)
{
Console.WriteLine("My event is ok!");
}
}
// 步骤4,用event关键字定义事件对象
private event MyEventHandler myevent;
private MyEventCls myecls;
public EventTest()
{
myecls = new MyEventCls();
// 步骤5,用+=操作符将事件添加到队列中
this.myevent += new MyEventHandler(myecls.MyEventFunc);
}
// 步骤6,以调用delegate的方式写事件触发函数
protected void OnMyEvent(System.EventArgs e)
{
if(myevent != null)
myevent(this, e);
}

public void RaiseEvent()
{
EventArgs e = new EventArgs();
// 步骤7,触发事件
OnMyEvent(e);
}

public static void Main()
{
EventTest et = new EventTest();
Console.Write("Please input a:");
string s = Console.ReadLine();
if(s == "a")
{
et.RaiseEvent();
}
else
{
Console.WriteLine("Error");
}
}
}

输出结果如下,黑体为用户的输入:
Please input ‘a’: a
My event is ok!

小结

通过上面的讨论,我们大体上明白了delegate和event的概念,以及如何在C#中使用它们。我个人认为,delegate在C#中是一个相当重要的概念,合理运用的话,可以使一些相当复杂的问题变得很简单。有时我甚至觉得,delegate甚至能够有指针的效果,除了不能直接访问物理地址。而且事件也是完全基于delegate来实现的。由于能力有限,本文只是对delegate和event的应用作了一个浅显的讨论,并不深入,我希望本文能够起到抛砖引玉的作用。真正想要对这两个概念有更深入的了解的话,还是推荐大家看MSDN。

Thursday, April 03, 2008

(ZT)Net中的集合类

 

.NetFramework提供了很多集合类,你有没有都使用过它们呢?

下面以总结的形式归纳一下它们.

((I)).集合类型
1.
一般集合
I.Array
a.Array
中的秩是Array中的维数.一个Array可以有一个或多个秩.
Array
具有固定的容量.如果有可变容量,则用Array.CreateInstance,其可以不从零开始存储.
II.ArrayList
集合类型
a.
是数组的复杂版本.Array是数组是固定的,ArrayList类是根据需要自动扩展的.如果更改了Array.Capacity属性的值,则自动进行内存重新分配和元素复制.
b.ArrayList
提供添加/或移除某一范围元素的方法.Array,只能一次获取或设置一个元素的值.
c.
使用Synchronized方法可以很容易地创建ArrayList的同步版本.Array将一直保持它,直到用户实现同步为止.
d.ArrayList
提供将只读和固定大小包装返回到集合的方法.Array不提供.
e.Array
提供ArrayList所不具有的某些灵活性.
I.
可以设置Array的下限,ArrayList的下限始终为零.
II.Array
可以具有多个维度,ArrayList始终是唯一的.
III.Array
是特定类型(不是Object),ArrayList性能好.ArrayList在存储和检索时经常发生拆箱和装箱操作现象.
III.
哈希表集合
a.Hashtable
类基于IDictionary接口,因此该集合中的每一元素是键和值对.
b.Object.GetHashCode
方法为其自身生成哈希代码.还可以通过使用Hashtable构造函数,为所有元素指定一个哈希函数.
IV.SortedList
集合类型
a.SortedList
类类似于HashtableArrayList间的混合.
b.SortedList
的每一元素都是键对值,提供只返回键列表或只返回值列表的方法.
c.
如果想要一个保留键和值的集合,并且还需要索引的灵活性,则使用SortList.
V.
队列集合类型
a.
如果需要以信息在集合中存储的相同顺序来访问这些信息,请使用Queue.
b.Enqueue
将一个元素添加到Queue的队尾.DequeueQueue处移除最旧的元素.PeekQueue的开始处返回最旧的元素,但不将从Queue中移除.
VI.
堆栈集合类型
a.
如果需要以信息在集合中存储的相反顺序来访问这些信息,请使用Queue.
b.Push
Stack的顶部插入一个元素.PopStack的顶部移除一个元素.Peek返回处于Stack顶部的元素,但不将其从栈顶上移除.
2.
位集合
I.BitArray
a.BitArray
是一个集合类,容量与计数相同.通过增加Length属性来将元素添加到BitArray;通过降低Length属性将元素删除.
b.
独特方法,And/Or/Xor/Not/SetAll.
c.
位于System.Collections.
II.BitVector32
a.
速度快,精确存储32,并且同时存储标志位和小整数.
b.
位于System.Collections.Specialized.
3.
专用集合
I.NameValueCollection
a.
基于NameObjectCollectionBase,NameValueCollection可以接受每个键多个值,NameObjectCollectionBase接受每个键,但只有一个值.

((2)).选择用哪种集合
***Queue
Stack:需要一个序列列表,其中的元素在检索后放弃.否则,用其它集合.
***Queue
Stack:按一定的顺序访问这些元素(先进先出,后进先出),如果随机,用其它集合.
***
是否通过索引访问每一个元素?
*ArrayList
StringCollection提供通过元素的从零开始的*索引*对其元素进行访问.
*(Hashtable)(SortedList)(ListDictionary)(StringDictionary)
提供通过元素的**对其元素进行访问
*(NameObjectCollectionBase)
(NameValueCollection)提供或者通过元素的从零开始的*索引*或者通过元素的**对其元素进行访问.
***
每一元素将包含一个值/一个值和一个键的组合还是一个键和多个值的组合?
*
一个值:使用基于IList的任何集合
*
一个键和一个值:使用基于IDictionary的任何集合.
*
一个键和多个值:使用System.Collections.Specialized命名空间中的NameValueCollection.
***
是否需要用与元素方式不同的方式对元素排序?
*Hashtable
通过键的哈希代码对元素进行排序.
*SortedList
基于IComparer实现,通过键对元素进行排序.
*ArrayList
提供Sort方法该方法将IComparer实现作为一个参数采用.
***
是否需要信息的快速搜索和检索?
*
对于小集合(十项或更少),ListDictionary快于Hashtable.
***
是否需要只接受字符串的集合?
*StringCollection(
基于IList)StringDictionary(基于IDictionary)位于System.Collections.Specialized命名空间中.

((3)).强类型集合

强类型集合类与上面讲的集合特性差不多一样,基本上都是一一对应的关系,只不过是强类型;并且命名空间在System.Collection.Generic下面. 不需要自定义强制类型集合,可以直接调用System.Collection.Generic命名空间下面的类

这里就不作多说.这里只保留了个以前讲课时的PPT.

1.幻灯片课程

下载:

http://www.cnblogs.com/Files/ChengKing/Asp.net%202.0%20Collection%20Study(ppt).rar

2.示例代码

下载:

http://www.cnblogs.com/Files/ChengKing/Collection(DonNet%202.0)(project).rar

Tuesday, April 01, 2008

Good Explaination about Dispose pattern (ZT)

 

1、Finalize方法(C#中是析构函数,以下称析构函数)是用于释放非托管资源的,而托管资源会由GC自动回收。所以,我们也可以这样来区分托管和非托管资源。所有会由GC自动回收的资源,就是托管的资源,而不能由GC自动回收的资源,就是非托管资源。在我们的类中直接使用非托管资源的情况很少,所以基本上不用我们写析构函数。
2、大部分的非托管资源会给系统带来很多负面影响,例如数据库连接不被释放就可能导致连接池中的可用数据库连接用尽。文件不关闭会导致其它进程无法读写这个文件等等。
实现模型:
1、由于大多数的非托管资源都要求可以手动释放,所以,我们应该专门为释放非托管资源公开一个方法。实现IDispose接口的Dispose方法是最好的模型,因为C#支持using语句快,可以在离开语句块时自动调用Dispose方法。
2、虽然可以手动释放非托管资源,我们仍然要在析构函数中释放非托管资源,这样才是安全的应用程序。否则如果因为程序员的疏忽忘记了手动释放非托管资源,那么就会带来灾难性的后果。所以说在析构函数中释放非托管资源,是一种补救的措施,至少对于大多数类来说是如此。
3、由于析构函数的调用将导致GC对对象回收的效率降低,所以如果已经完成了析构函数该干的事情(例如释放非托管资源),就应当使用SuppressFinalize方法告诉GC不需要再执行某个对象的析构函数。
4、析构函数中只能释放非托管资源而不能对任何托管的对象/资源进行操作。因为你无法预测析构函数的运行时机,所以,当析构函数被执行的时候,也许你进行操作的托管资源已经被释放了。这样将导致严重的后果。
5、(这是一个规则)如果一个类拥有一个实现了IDispose接口类型的成员,并创建(注意是创建,而不是接收,必须是由类自己创建)它的实例对象,则这个类也应该实现IDispose接口,并在Dispose方法中调用所有实现了IDispose接口的成员的Dispose方法。
只有这样的才能保证所有实现了IDispose接口的类的对象的Dispose方法能够被调用到,确保可以手动释放任何需要释放的资源。