public class MyCollection { public delegate bool SelectItem(string sItem); public string[] GetFilteredItemArray(SelectItem itemFilter) { Liststring sList = new Liststring(); foreach(string sItem in m_sList) { if (itemFilter(sItem) == true) sList.Add(sItem); } return sList.ToArray(); }
public Liststring ItemList { get { return m_sList; } } private Liststring m_sList = new Liststring(); } 我们可以用上面定义的类写如下所示的代码:
public class Program { public static void Main(string[] args) { MyCollection objMyCol = new MyCollection(); objMyCol.ItemList.Add("Aditya"); objMyCol.ItemList.Add("Tanu"); objMyCol.ItemList.Add("Manoj"); objMyCol.ItemList.Add("Ahan"); objMyCol.ItemList.Add("Hasi");
//获得集合中以字母’A‘开头的字符项数组 string[] AStrings = objMyCol.GetFilteredItemArray(FilterStringWithA); Console.WriteLine("----- Strings starting with letter ''A'' -----"); foreach(string s in AStrings) { Console.WriteLine(s); } //获得集合中以字母’T‘开头的字符项数组 string[] TStrings = objMyCol.GetFilteredItemArray(FilterStringWithT); Console.WriteLine("----- Strings starting with letter ''T'' -----"); foreach(string s in TStrings) { Console.WriteLine(s); } }
public static bool FilterStringWithA(string sItem) { if (sItem[0] == ''A'') return true; else return false; } public static bool FilterStringWithT(string sItem) { if (sItem[0] == ''T'') return true; else return false; } } 可以看出对于每个我们想要提供的简单过滤准则,我们应该定义一个方法(静态或实例的)。这很快就搞乱了类的代码。而用匿名方法,代码变得相当自然。下面是这个Program类用匿名方法重写后的:
public class Program { public delegate void MyDelegate(); public static void Main(string[] args) { MyCollection objMyCol = new MyCollection(); objMyCol.ItemList.Add("Aditya"); objMyCol.ItemList.Add("Tanu"); objMyCol.ItemList.Add("Manoj"); objMyCol.ItemList.Add("Ahan"); objMyCol.ItemList.Add("Hasi"); //获得集合中以字母’A‘开头的字符项数组 string[] AStrings = objMyCol.GetFilteredItemArray(delegate(string sItem) { if (sItem[0] == ''A'') return true; else return false; }); Console.WriteLine("----- Strings starting with letter ''A'' -----"); foreach (string s in AStrings) { Console.WriteLine(s); } //获得集合中以字母’ T ‘开头的字符项数组 string[] TStrings = objMyCol.GetFilteredItemArray(delegate(string sItem) { if (sItem[0] == ''T'') return true; else return false; }); Console.WriteLine("----- Strings starting with letter ''T'' -----"); foreach (string s in TStrings) { Console.WriteLine(s); } } } 正如上面示例中的所示,我们已能用内联代码块定义的过滤准则替代定义一个新的方法来代表每个过滤准则。老实说,用这种内联代码可能看起来自然并且避免了定义新方法,但是如果这个技术被用于更大的内联代码块,这时代码很快变得难于管理并可能导致代码重复。因此,使用方法与内联匿名方法都是委托/事件处理器的可选方案。
public class Program { public delegate void MyDelegate(); public static void Main(string[] args) { //实例数据成员测试 Program p = new Program(); for(int i=1;i=5;i++) p.TestInstanceDataMembers(); } public void TestInstanceDataMembers() { MyDelegate d = delegate { Console.WriteLine("Count: {0}",++m_iCount); }; d(); } public int m_iCount = 0; } 我们定义了一个新的实例方法:TestInstanceDataMembers,在''Program''类中这个方法定义了一个匿名方法,匿名方法使用了实例数据成员:隶属''Program''类的m_iCount。当这个示例编译时,C#编译器将创建一个private实例方法来包装这个在TestInstanceDataMembers中定义的匿名方法。C#编译器必须创建一个实例方法因为该方法需要访问''Program''类的实例数据成员。下面是这个示例程序集''Program''类的ILDASM视图。在图的下部选中部分显示了由C#编译器默默添加到''Program''类的新的private实例方法。
在循环控制结构内使用匿名方法的局部变量的用法
当处理循环控制结构时将局部变量封装入类的数据成员有着有趣但危险的一面,让我们看看下面代码:
public class Program { public delegate void MyDelegate(); public static void Main(string[] args) { MyDelegate d = null; for (int i = 1; i = 5; i++) { MyDelegate tempD = delegate { Console.WriteLine(i); }; d += tempD; } d(); } } 上面的代码运行时将会有什么输出呢?我们的意图是捕获在我们的匿名方法中的循环计数变量''i''并显示之。我们预期的输出应该如下所示:
public class Program { public delegate void MyDelegate(); public static void Main(string[] args) { MyDelegate d = null; for (int i = 1; i = 5; i++) { int k = i; MyDelegate tempD = delegate { Console.WriteLine(k); }; d += tempD; } d(); } } 在你运行上面的代码示例时,将会获得预期的输出,也就是:
public class Program { public delegate void MyDelegate(); public static void Main(string[] args) { int iTemp = 100; MyDelegate dlg = delegate { Console.WriteLine(iTemp); }; dlg(); } } 对于我们到现在为止对匿名方法已了解的内容来说,这段代码不应该编译。因为我们没有使用如何实例数据成员,C#编译器应该在''Program''类中创建一个private静态方法来包装这个匿名方法。但是新的方法如何访问局部变量呢?这让我们相信该代码将不能被编译。但是令人惊讶的是,C#编译器成功编译了这个代码而没有任何错误或报警。而且,当你执行这个示例时,在控制台屏幕上输出打印出iTemp变量的正确的值。现在让我们进入匿名方法的高级话题。一个匿名方法有封装在其方法体中使用了的环境变量的值的能力。这个封装应用于匿名方法被定义的方法中的所有局部变量。当C#编译器在一个匿名方法的方法体中识别出用到一个局部变量,它就会做如下事情:
public class Program { private class InnerClass { private void InstanceMethod() { Console.WriteLine(iTemp); } public int iTemp; } public delegate void MyDelegate(); public static void Main(string[] args) { InnerClass localObject = new InnerClass(); localObject.iTemp = 100; MyDelegate dlg = new MyDelegate(localObject.InstanceMethod); dlg(); } } 正如上面的伪代码所示,C#编译器为''Program''类生成了一个private内部类。在匿名方法中使用的局部变量作为新的已创建的内部类的一个实例数据成员而捕获。并且匿名方法本身被包装在内部类的实例方法中。最后,该实例方法在Main方法中作为一个委托处理器而使用。这样,当委托被调用时,对于在被封装入匿名方法中的局部变量将会有一个正确的值。下面图中选定的部分显示了由C#编译器默默添加到''Program'' 类的新的private内部类。
public class Program { public delegate void MyDelegate(); public static void Main(string[] args) { MyDelegate dlg = null; int iTemp = 100; if (iTemp 50) { int jTemp = 200; dlg = delegate { Console.WriteLine("iTemp: {0}, jTemp: {1}",iTemp,jTemp); }; } dlg(); } } 当上面的代码被编译时,C#编译器在''Program''类中创建两个内部类。一个内部类包装局部变量iTemp作为一个public数据成员。第二个内部类包装在嵌套作用域中的局部变量,jTemp,作为一个public数据成员,同时在相同的嵌套作用域中包装匿名方法作为public实例方法。C#编译器为上面的代码生成下面的伪代码:
public class Program { //包装来自外部作用域的局部变量''iTemp''的类 private class InnerClassScope1 { public int iTemp; } //包装来自内部作用域和匿名方法的局部变量的类 private class InnerClassScope2 { public void InstanceMethod() { Console.WriteLine("iTemp: {0}, jTemp: {1}", localObjectScope1.iTemp, jTemp); } public InnerClassScope1 localObjectScope1; public int jTemp; } public delegate void MyDelegate(); public static void Main(string[] args) { MyDelegate dlg = null; InnerClassScope1 localObject1 = new InnerClassScope1(); localObject1.iTemp = 100; if (localObject1.iTemp 50) { InnerClassScope2 localObject2 = new InnerClassScope2(); localObject2.localObjectScope1 = localObject1; localObject2.jTemp = 200; dlg = new MyDelegate(localObject2.InstanceMethod); } dlg(); } } 正如上面的代码所示,包装匿名方法的内部类将拥有所有代表外部作用域局部变量的对象,这些变量被用在匿名方法中,像public数据成员。下图显示了C#默默创建的内部类的ILDASM视图:
在下面的练习中,将创建一个应用程序,它包含的一个方法能够计算一名顾问的收费金额假定该顾问每天收取固定的费用,将根据工作了多少天来收费。首先要开发应用程序的逻辑,然后利用生成方法存根向导来写出这个逻辑使用的方法。接着,我们将在一个控制台应用程序中运行方法,以获得对该程序的最终印象。最后,我们将使用Visual Studio 2005...