Leo Zhang

A simple man with my own ideal

关于C#中Anonymous Method---匿名方法的基础知识

Anonymous Method--匿名方法说的是不对方法进行显式定义而将方法的执行代码直接封装到Delegate对象中,这样做的好处不仅仅是简化代码这么简单,它还可以帮助你进行多个方法间的状态共享和将代码段作为参数进行传递。

一、Anonymous Method初探

下面看一个不使用Anonymous Method的例子:

using System;

using System.Collections.Generic;

using System.Windows.Forms;

namespace Anonymous_Method
{
    
static class Program
    {
        
/// <summary>
        
/// 应用程序的主入口点。
        
/// </summary>
        [STAThread]
        
static void Main()
        {
            String[] names 
="Jeff""Kristin""Adian" };
            names 
= Array.FindAll(names, new Predicate<String>(NamePredicate));          
            names 
= Array.ConvertAll<String, String>(names, new Converter<String,String>(NameConvertPredicate));
            Array.Sort(names, String.Compare);
            Array.ForEach(names, Console.WriteLine);

            Console.ReadKey();
        }

        
/// <summary>
        
/// Predicates the specified name.
        
/// </summary>
        
/// <param name="name">The name.</param>
        
/// <returns>true or false</returns>
        static Boolean NamePredicate(String name)
        {
            
return name.IndexOf('i'>= 0;
        }

        
/// <summary>
        
/// Names the convert predicate.
        
/// </summary>
        
/// <param name="name">The name.</param>
        
/// <returns>String</returns>
        static String NameConvertPredicate(String name)
        {
            
return name.ToUpper();
        }
    }
}

其中Array.FindAll原型为:

                public static T[] FindAll<T>(T[] array, Predicate<T> match);

    Predicate是一个delegate,表示定义一组条件并确定指定对象是否符合这些条件的方法。原型为:

                public delegate bool Predicate<T>(T obj);

    Array.ConvertAll原型为:

                public static TOutput[] ConvertAll<TInput, TOutput>(TInput[] array, Converter<TInput, TOutput> converter);

    Converter<TInput, TOutput>是一个delegate,表示将对象从一种类型转换为另一种类型的方法。原型为:

                public delegate TOutput Converter<TInput, TOutput>(TInput input);

       如果像Array.FindAll这样的方法用到多次,比如我接下来要在names中查找字符'K',那么就需要再写一个类似NamePredicate的命名方法,可以想象这将是多么郁闷的一件事情,这时我们可以利用Anonymous Method来简化编码提高生产率,代码如下:

using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace Anonymous_Method
{
    
static class Program
    {
        
/// <summary>
        
/// 应用程序的主入口点。
        
/// </summary>
        [STAThread]
        
static void Main()
        {
            String[] names 
="Jeff""Kristin""Adian" };

            Char charToFind 
= 'i';
            names 
= Array.FindAll(names, delegate(String name) { return name.IndexOf(charToFind) >= 0; });
            names 
= Array.ConvertAll<String, String>(names, delegate(String name) { return name.ToUpper(); });
            Array.Sort(names, String.Compare);
            Array.ForEach(names, Console.WriteLine);

            Console.ReadKey();
        }
    }
}

 

Anonymous Method好处显而易见,当然,我认为在方法的代码行数不多的情况下使用Anonymous Method比较合适。

二、深入了解Delegate类

Delegate类是一个抽象类,delegate关键字定义的类型都属于Delegate类的非抽象派生类,Delegate类的定义如下:

[Serializable, ClassInterface(ClassInterfaceType.AutoDual)]
public abstract class Delegate : ICloneable, ISerializable
{
    
// Fields
    private RuntimeMethodInfo _method;
    
private IntPtr _methodPtr;
    
private IntPtr _methodPtrAux;
    
private object _target;

    
// Methods
    private Delegate();
    
protected Delegate(object target, string method);
    
protected Delegate(Type target, string method);
    
public virtual object Clone();
    
public static Delegate Combine(Delegate[] delegates);
    
public static Delegate Combine(Delegate a, Delegate b);
    
protected virtual Delegate CombineImpl(Delegate d);
    
public static Delegate CreateDelegate(Type type, MethodInfo method);
    
public static Delegate CreateDelegate(Type type, object target, string method);
    
public static Delegate CreateDelegate(Type type, Type target, string method);
    
public static Delegate CreateDelegate(Type type, object target, string method, bool ignoreCase);
    
public object DynamicInvoke(object[] args);
    
protected virtual object DynamicInvokeImpl(object[] args);
    
public override bool Equals(object obj);
    
public override int GetHashCode();
    
public virtual Delegate[] GetInvocationList();
    
protected virtual MethodInfo GetMethodImpl();
    
public virtual void GetObjectData(SerializationInfo info, StreamingContext context);
    [MethodImpl(MethodImplOptions.InternalCall)]
    
private static extern Delegate InternalAlloc(RuntimeType type);
    [MethodImpl(MethodImplOptions.InternalCall)]
    
internal extern void InternalCreate(object target, string method, bool ignoreCase);
    [MethodImpl(MethodImplOptions.InternalCall)]
    
internal extern void InternalCreateMethod(RuntimeMethodInfo invokeMeth, RuntimeMethodInfo targetMethod);
    [MethodImpl(MethodImplOptions.InternalCall)]
    
internal extern void InternalCreateStatic(RuntimeType target, string method);
    [MethodImpl(MethodImplOptions.InternalCall)]
    
internal extern RuntimeMethodInfo InternalFindMethodInfo();
    
private bool IsStatic();
    
public static bool operator ==(Delegate d1, Delegate d2);
    
public static bool operator !=(Delegate d1, Delegate d2);
    
public static Delegate Remove(Delegate source, Delegate value);
    
public static Delegate RemoveAll(Delegate source, Delegate value);
    
protected virtual Delegate RemoveImpl(Delegate d);

    
// Properties
    public MethodInfo Method { get; }
    
public object Target { get; }
}

 

 

对于delegate关键字定义的类型,其实例的创建方式有3种:

1、利用new关键字来实现,如上例中的new Predicate<String>(NamePredicate);要求所封装方法的签名与delegate类型一致。

2、Anonymous Method,如上例中的 names = Array.FindAll(names, delegate(String name) {  return name.IndexOf(charToFind) >= 0;  });

要求所封要求参数列表和执行代码的返回类型与delegate类型保持一致。

3、较少用,通过Delegate类提供的静态方法CreateDelegate来创建。

Delegate类提供了两个公共只读属性:Target和Method,分别代表调用方法的对象和所封装的方法,对于Named Method,其Target属性值为调用方法的当前对象,Method属性值为实例化delegate对象时指定的方法;当方法为静态时,Target属性值为null。对于Anonymous Method,其Target属性值始终为null,Method属性值为赋值给delegate对象的匿名方法所创建的方法。

当我们定义一个delegate类型的时候,如: public delegate bool Predicate<T>(T obj);编译器其实会将它编译为像下面的一个类:

public class Predicate<T> : System.MulticastDelegate
{
        
public Predicate(Object @object, IntPtr method);

        
public virtual IAsyncResult BeginInvoke(T obj, AsyncCallback callback, Object @object);

        
public virtual Boolean EndInvoke(IAsyncResult result);

        
public virtual Boolean Invoke(T obj);

}

 

可以看到该类继承于FCL中定义的System.MulticastDelegate类,而System.MulticastDelegate又继承于Delegate类,所有delegate类都是继承于System.MulticastDelegate类的。想更为深入了解delegate可以参考Jeffrey Richter的大作:《CLR.via.C#.Second.Edition》。

三、Anonymous Method的定义规则

  Anonymous Method由3个部分组成:关键字delegate,parameters列表和code section,其中parameters可以省略。

Anonymous Method表达式本身不能成为一个完整的语句,需要将它赋值给一个delegate对象,而且要求Anonymous Method表达式与delegate的定义一致:Anonymous Method返回类型和delegate定义类型相同或者可以隐式转换为delegate定义类型的返回类型;Anonymous Method的参数列表可以省略,但是如果有out参数则不能省略;如果Anonymous Method表达式指定了参数列表,则参数列表要与delegate定义类型的参数列表相同,且每个参数的类型与delegate中对应的类型要相同或者可以隐式转换;Anonymous Method表达式参数列表中不能有params所修饰的数组型参数。需要注意省略参数列表和参数列表为空时两个完全不同的概念。

四、Anonymous Method的外部变量

Anonymous Method表达式所在代码段中出现的所有变量。如下:

using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace Anonymous_Method
{
    
public delegate Int32 SubDelegate(Int32 x);

    
static class Program
    {
        
/// <summary>
        
/// 应用程序的主入口点。
        
/// </summary>
        [STAThread]
        
static void Main()
        {
            Int32 x 
= 5;
            
//Named Method
            SubDelegate d1 = new SubDelegate(Sub);
            Console.Write(d1(x));
            Console.Write(x);
            Console.Write(d1(x));
            Console.WriteLine(x);

            SubDelegate d2 
= delegate { return x--; };
            Console.Write(d2(x));
            Console.Write(x);
            Console.Write(d2(x));
            Console.WriteLine(x);

            Console.ReadKey();
        }

        
/// <summary>
        
/// Subs the specified x.
        
/// </summary>
        
/// <param name="x">The x.</param>
        
/// <returns></returns>
        static Int32 Sub(Int32 x)
        {
            
return x--;
        }
    }
}

 

这里x就是外部变量,上面代码的输出为:5555

                                   5443 

      另外需要注意的就是外部变量要想在Anonymous Method中使用就必须是已经赋值的。由此可见多个方法可以通过外部变量来进行状态共享和消息传递。

      此外Anonymous Method还能作为方法参数和返回值,在event中也可以使用Anonymous Method。

五、总结

      Anonymous Method所带来的最大优越性就是可以将一段代码直接作为参数来使用而不用显式的去定义一个方法,可以简化代码,提高工作效率,根据Jeffrey Richter的经验,Anonymous Method中应该包含3行以内的代码。Anonymous Method的其他用途还望各大高手补充。


 

 

 

posted on 2009-05-08 15:16  Leo Zhang  阅读(1068)  评论(1编辑  收藏  举报

导航