65.9K
CodeProject 正在变化。 阅读更多。
Home

LinFu 框架介绍,第二部分:LinFu.DynamicObject – 为静态类型语言添加动态语言特性

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.97/5 (47投票s)

2007年10月26日

LGPL3

16分钟阅读

viewsIcon

149250

downloadIcon

934

使用 LinFu.DynamicObject 为您喜欢的 .NET 语言添加混入(mixins)、鸭子类型(duck typing)和多重分派(multiple dispatch)

Screenshot - Duck.jpg

引言

在本系列文章的这一部分,我将向您展示如何使用 LinFu.DynamicObject 为您的代码添加动态语言特性。我还会讨论 LinFu.DynamicObject 在后台是如何实现这一点的,并简要介绍如何在 C# 3.0 即将推出的新语言特性中结合使用它。注意:如果您想了解整个 LinFu 框架,请点击此处

背景

自 .NET Framework 1.0 版本发布以来,许多开发人员一直在呼吁支持多重继承和混入(mixins)等特性。不幸的是,由于实现多重继承、混入及其带来的麻烦的复杂性,Microsoft 在 .NET Framework 中省略了这些特性。

直到最近,这些功能请求基本上都被忽略了。然而,得益于 IronPython 和 IronRuby 等新兴的动态语言,Microsoft 终于开始提供大家一直在寻求的语言特性。但是,要在您的应用程序中使用这些特性,您必须将代码库切换到一种新的编程语言。如果存在截止日期和必须达到的里程碑,这可能不是一个可行的选择。

这种情况有效地给我们留下了两个选择:要么找到一种方法来规避 C#、VB.NET 和 CLR 所施加的静态类型、单一继承的限制,要么就得自己动手使用 Reflection.Emit 来实现这些特性。大多数理智的开发者都会选择第一种方案。谢天谢地,我在编码方面不是一个理智的人,因此 LinFu.DynamicObject 应运而生。

您需要了解的内容

本文假定您对混入、多重分派等动态语言特性有一定的了解。它还假定您对动态代理有足够的理解,以便能够使用该库。

特性和用法

LinFu.DynamicObject 以及 LinFu 库其余部分最显著的特点之一是它与语言无关。尽管这里的示例是用 C# 完成的,但任何人都可以轻松地在其他语言(如 VB.NET)中以相同的方式使用它。除了您想使用的编程语言之外,LinFu.DynamicObject 还支持以下特性:

延迟绑定

与应用程序的典型方法调用不同,LinFu.DynamicObject 在应用程序运行时才知道您将使用哪个方法。LinFu.DynamicObject 实际上是通过查看您调用的方法的名称以及传递给该方法的运行时参数来确定您将使用哪个方法。

这是一个例子。假设我有一个 System.IO.StringWriter 类的实例,它有多个 WriteLine() 方法的重载。

StringWriter writer = new StringWriter();
DynamicObject dynamic = new DynamicObject(writer);

// Note that there are 18 overloads for TextWriter.WriteLine()

// Call WriteLine(string text);
dynamic.Methods["WriteLine"]("Hello, World!");

// Call Writeline(bool value);
dynamic.Methods["WriteLine"](false);

无论方法签名如何,LinFu.DynamicObject 都能轻松地在运行时对对象执行延迟绑定的调用。

多重分派

如我之前提到的,LinFu.DynamicObject 可以匹配任何方法签名和任意数量的参数。利用这一特性,我可以做一些仅凭静态语言的本机特性就很难(或不可能)实现的事情。例如,假设我有一个名为 CollisionManager 的类,它管理屏幕上的许多形状,并且当一组特定的形状 Collide() 时,我希望它执行一组特定的操作。

public class CollisionManager
{
   // Overload #1
   public void Collide(Circle circle, Square square, Triangle triangle)
   {
      Console.WriteLine("Overload #1 Called");
   }

   // Overload #2
   public void Collide(Square square, Circle circle, Triangle triangle)
   {
      Console.WriteLine("Overload #2 Called");
   }

   // Overload #3
   public void Collide(Triangle triangle, Square square, Circle circle)
   {
      Console.WriteLine("Overload #3 Called");
   }

   // Overload #4
   public void Collide(Triangle triangle, Circle circle)
   {
      Console.WriteLine("Overload #4 Called");
   }
}

与疯狂的碰撞

现在,假设我想能够调用这些重载中的任何一个,具体取决于每个参数的顺序和类型,并且只使用一个方法调用。毋庸置疑,在静态类型语言中执行此类操作简直是噩梦。幸运的是,有了 LinFu.DynamicObject,您不再需要手动执行此操作。调用这些重载方法就像以下方式一样简单:

// Wrap the CollisionManager instance
DynamicObject dynamic = new DynamicObject(new CollisionManager());

object[] list1 = new object[]{new Circle(), new Square(), new Triangle()};
object[] list2 = new object[]{new Square(), new Circle(), new Triangle()};
object[] list3 = new object[]{new Triangle(), new Square(), new Circle()};
object[] list4 = new object[]{new Circle(), new Triangle()};

// Call Overload #1
dynamic.Methods["Collide"](list1);

// Call Overload #2
dynamic.Methods["Collide"](list2);

// Call Overload #3
dynamic.Methods["Collide"](list3);

// Call Overload #4
dynamic.Methods["Collide"](list4);

从上面的示例可以看出,LinFu.DynamicObject 可以轻松调用适当的 Collide() 方法,而不论该方法可能存在的重载数量如何。它可以轻松地区分不同的方法签名。它为开发人员处理了所有样板代码,让您可以专注于更重要的任务。

动态方法

DynamicObject 还允许您使用以下方法在运行时动态地向其添加方法:

public void AddMethod(string methodName, MulticastDelegate body);
public void AddMethod(string methodName, CustomDelegate body, 
    Type returnType,   params Type[] parameters);

第一个重载允许您使用现有的委托类型(例如编译时存在的委托类型)添加方法,而第二个重载允许您定义具有特定签名和方法体的​​方法。我将在下面逐一介绍每个示例:

使用现有的委托类型

public delegate int MathOperation(int a, int b);

public class Program
{
   public static void Main()
   {
       // Define the method body
       MathOperation body = delegate(int a, int b) { return a + b; };

       // Create an empty DynamicObject
       // that wraps around a System.Object
       DynamicObject dynamic = new DynamicObject();
       dynamic.AddMethod("Add", body);

       // This will return '2'
       int result = (int)dynamic.Methods["Add"](1, 1);
   }
}

在此示例中,我使用了类型为 MathOperation 的匿名委托,并为其提供了方法体。使用该方法体,然后我在 DynamicObject 上创建了一个名为 Add 的方法。一旦添加了方法,剩下的唯一工作就是调用它,返回 2 作为结果。如果您已经拥有特定委托的实例(例如匿名委托),并希望使其成为 DynamicObject 的一部分,那么使用现有的委托类型可能会派上用场。这种方法的唯一问题是它只允许您添加基于编译时存在的委托类型的​​方法。这可能会阻止开发人员在运行时生成方法。这时就可以使用第二个重载了:

使用 CustomDelegate 类型

// Note: The differences are highlighted in bold
public class Program
{
   public static void Main()
   {
       // Notice there isn't a preexisting
         // MathOperation delegate in this example
       CustomDelegate body = delegate(object[] args)
       {
          int a = (int)args[0];
          int b = (int)args[1];
          return a + b;
       };

       // Create an empty DynamicObject
       // that wraps around a System.Object
       DynamicObject dynamic = new DynamicObject();
       Type[] parameters = new Type[] {typeof (int), typeof (int)};
       Type returnType = typeof (int);

       dynamic.AddMethod("Add", body, returnType, parameters);

       // This will return '2'
       int result = (int)dynamic.Methods["Add"](1, 1);
   }
}

使用 CustomDelegate 类型允许我定义方法体,而无需将方法签名绑定到任何特定的参数类型。在此示例中,我从 CustomDelegate 实例的 body 中定义的 args 数组中提取了前两个参数。就像在上一个示例中一样,我只是将两个数字相加并返回结果。此示例的区别在于,当我使用 CustomDelegate 类型作为方法体时,我实际上必须同时指定方法的返回类型和方法的参数类型。

选择权在您

除了我刚才提到的那些差异之外,这两个重载的工作方式相同。您是否更喜欢 CustomDelegate 的灵活性,还是使用您自己预定义的委托的签名,完全取决于您。依我之见,第二个选择是更好的选择,因为它更具动态性,因此更易于维护。

鸭子类型

LinFu.DynamicObject 的另一个优点是它可以看起来和行为像几乎任何接口或非密封类。例如,假设我有以下接口定义:

public interface IMath
{
   int Add(int a, int b);
}

像鸭子一样行走

借鉴上一节的示例,我可以采用相同的 DynamicObject 并使其看起来和行为像 IMath

IMath math = null;

// Make sure that the dynamic object
// can indeed act like an IMath object
int result = 0;
if(dynamic.LooksLike<IMath>())
{
   math = dynamic.CreateDuck<IMath>();
   result = math.Add(1, 1);
}

每次调用 CreateDuck() 方法时,DynamicObject 都会生成一个实现给定类或接口(例如 IMath)的代理。然后,该代理会将所有调用转发回 DynamicObject 来处理实际的方法实现。因此,每次客户端调用 IMath.Add() 方法时,代理都会使用以下方法调用直接调用 DynamicObject

// This is equivalent to IMath.Add(1, 1)
return dynamic.Methods["Add"](1,1);

是鸭子还是不是鸭子?

除了像 IMath 实例那样运作之外,DynamicObject 还可以使用 LooksLike() 方法确定它是否具有足够数量的兼容方法和属性来实现该接口。如果您想确保 DynamicObject 符合特定的接口类型,这将非常有用。

Mixins

LinFu.DynamicObject 还支持“混入”(或组合)自身与其他对象实例,使其看起来好像所有混入的项在运行时形成一个连贯的类。总的来说,它支持三种类型的混入:

标准对象引用

DynamicObject 实例可以与另一个对象引用(即普通的 System.Object 派生类)混入自身,获取该类的所有方法和属性,并使其成为 DynamicObject 实例本身的一部分。例如,假设我想实现一个名为 IPerson 的接口:

public interface IPerson
{
   string Name { get; }
   int Age { get; }
}

……假设我想使用两个假设的混入实例 NameableHasAge 来实现该接口:

public class Nameable
{
   private string _name;

   public Nameable(string name)
   {
     _name = name;
   }
   public string Name
   {
     get { return _name;  }
   }
}

public class HasAge
{
   private int _age;
   public HasAge(int age)
   {
     _age = age;
   }
   public int Age
   {
      get { return _age; }
   }
}

如您所见,这两个混入并没有什么特别之处。创建 IPerson 接口的实现就像这样简单:

DynamicObject dynamic = new DynamicObject();

IPerson person = null;

// This will return false
bool isPerson = dynamic.LooksLike<IPerson>();

// Implement IPerson
dynamic.MixWith(new HasAge(18));
dynamic.MixWith(new Nameable("Me"));

// Now that it's implemented, this
// will be true
isPerson = dynamic.LooksLike<IPerson>();

if (isPerson)
   person = dynamic.CreateDuck<IPerson>();

// This will return "Me"
string name = person.Name;

// This will return '18'
int age = person.Age;

上述示例适用于混入仅为属性持有者的简单情况,但如果混入具有更复杂的操作,需要访问 DynamicObject 呢?

感知混入的实例

对于更高级的场景,混入本身可能需要与当前 DynamicObject 实例的其他混入进行交互。如果您需要让混入与 DynamicObject 本身进行交互,您需要做的就是使用以下接口来实现您的混入:

public interface IMixinAware
{
    DynamicObject Self { get; set; }
}

public class MyMixin : IMixinAware
{
  private DynamicObject _self;
  public void DoSomething()
  {
    DynamicObject dynamic = Self;
    // Perform some operations on the DynamicObject
    IMath math = dynamic.CreateDuck<IMath>();
    math.Add(1, 1);
  }
  public DynamicObject Self
  {
    get { return _self; }
    set { _self = value;}
  }
}

public class MathMixin : IMath
{
   public int Add(int a, int b)
   {
      return a + b;
   }
}

接下来,您需要使用新的混入类调用 MixWith() 方法:

// ...somewhere in your program...
DynamicObject dynamic = new DynamicObject();
dynamic.MixWith(new MathMixin());

// Note: Once the MixWith() method is called,
// the DynamicObject will actually call
// IMixinAware.Self = this; on the new mixin
dynamic.MixWith(new MyMixin());

实现了 IMixinAware 接口的任何混入类都能够随时随地访问(并修改)附加的 DynamicObject。依我之见,这项特性赋予的力量是惊人的。目前,我认为我将把这留给读者来思考如何处理。总之,让我们进入下一部分:混入 DynamicObject 与其他 DynamicObject

组合 DynamicObjects

组合两个 DynamicObjects 就像这样做一样简单:

public class GreeterMixin
{
    public void Greet()
    {
       Console.WriteLine("Hello, World!");
    }
}
public class OtherMixin
{
    public void DoSomething()
    {
       Console.WriteLine("DoSomething() called");
    }
}

class Program
{
    static void Main(string[] args)
    {
       DynamicObject first = new DynamicObject(new GreeterMixin());
       DynamicObject second = new DynamicObject(new OtherMixin());

       // The two DynamicObjects can combine into a single object
       DynamicObject combined = first + second;
       combined.Methods["Greet"]();
       combined.Methods["DoSomething"]();
    }
}

当两个 DynamicObjects 组合时,会生成一个新的 DynamicObject,它具有前两个实例的所有方法和混入。然而,这可能会导致一个问题,如果两个动态对象具有相同类型的混入。在发生混入冲突时,使用哪个混入?

解决重复混入冲突

没有简单的解决办法。事实上,这可能是 Microsoft 将混入和多重继承排除在 CLR 之外的原因之一。因此,在没有完美解决方案的情况下,我决定采取最简单的方法并将其应用于 LinFu.DynamicObjectDynamicObject 中的第一个混入先执行。这是一个例子:

public class GreeterMixin
{
    private string _message;
    public GreeterMixin(string message)
    {
      _message = message;
    }
    public void Greet()
    {
       Console.WriteLine(_message);
    }
}

public static class Program
{
   public static void Main()
   {
      GreeterMixin first = new GreeterMixin("Hello, World!");
      GreeterMixin second = new GreeterMixin("Hello, CodeProject!");

      DynamicObject dynamic = new DynamicObject();
      dynamic.MixWith(first);
      dynamic.MixWith(second);

      // This will display "Hello, World!"
      dynamic.Methods["Greet"]();
   }
}

在这种情况下,第一个 GreeterMixin 将始终是第一个也是唯一执行的混入。这是因为 DynamicObject 将搜索其所有混入和方法,查找具有相同方法名称和兼容方法签名的第一个方法,并且只执行该方法。之后找到的每个方法都将被忽略。

关注点

绕过密封方法和类型:“鸭子胶带”

许多开发者在使用各种类库时遇到的一个问题是,库作者会密封一个类型,阻止其方法被覆盖。在正常情况下(即在静态类型语言中),这就像撞上一堵砖墙。幸运的是,对于 LinFu.DynamicObject 来说,情况并非如此。通常,您可以使用以下步骤绕过密封类型:

创建(或查找)一个看起来像目标类型的接口

您需要做的第一件事是确定您将在目标类中重写哪些方法或属性。然后创建一个与您将要重写的方法列表相似的接口。在此示例中,假设我想重写 System.Data.SqlConnection,以便在我调用 IDbConnection.Open() 时收到通知。由于所有连接都继承自 System.Data.IDbConnection(更重要的是,它们看起来都像 IDbConnection),因此我可以使用该接口作为用于鸭子类型的类型。

包装密封类实例并创建鸭子

DynamicObject dynamic = new DynamicObject(new SqlConnection());

// Note that this action doesn't actually cast it to an IDbConnection;
// it just makes it *act* like one
IDbConnection duck = dynamic.CreateDuck<IDbConnection>();

重写鸭子

接下来,我们需要创建一个包装目标方法或方法的拦截器。在这种情况下,我们希望包装 IDbConnection.Open()

public class CustomInterceptor : IInvokeWrapper
{
   private object _target;
   public class CustomInterceptor(object target)
   {
      _target = target;
   }
   public void BeforeInvoke(InvocationInfo info)
   {
      if (info.TargetMethod.Name != "Open")
          return;

      // Add the additional behavior here
      Console.WriteLine("Open Method Called!");
   }

    public object DoInvoke(InvocationInfo info)
    {
        // Call the original implementation
        object result = info.TargetMethod.Invoke(_target, info.Arguments);

        return result;
    }

    public void AfterInvoke(InvocationInfo info, object returnValue)
    {
        // Do nothing
    }
}

一旦定义了拦截器,接下来要做的就是为 IDbConnection 创建一个代理。然后将鸭子和 CustomInterceptor 绑定到一个实例中:

ProxyFactory factory = new ProxyFactory();
IDbConnection unsealed = 
    factory.CreateProxy<IDbConnection>(new CustomInterceptor(duck));

// Once the Open() method is called, it will now print "Open Method Called!"
unsealed.Open();

……这就是如何取消密封密封类型。

设计拦截器

对于那些使用 LinFuDynamicProxy 的人来说,您可能会发现有趣的一点是 DynamicObject 本身就是拦截器。这意味着任何 DynamicObject 都可以作为 LinFu.DynamicProxyProxyFactory 类创建的任何代理的拦截器。例如,假设我内存中已有一个代理,并且我想创建一个动态拦截器,以便将其附加到代理上:

ProxyFactory factory = new ProxyFactory();
IMath math = factory.CreateProxy<IMath>();

// Create the dynamic interceptor
DynamicObject interceptor = new DynamicObject();

// Attach it to the proxy
IProxy proxy = math as IProxy;
proxy.Interceptor = interceptor;

// Now the proxy is pointing back to the DynamicObject;
// This is where you would start dynamically adding methods
// to that DynamicObject
...

这种方法可以轻松地在应用程序运行时逐步构建拦截器实现。可能性是无限的。

将 DynamicObject 与 C# 3.0 结合使用

当您将 LinFu.DynamicObject 的功能与 C# 3.0 即将推出的语言特性相结合时,又会开启新的可能性。其中一些可能性是:

使用匿名类型实现混入

既然 C# 3.0 支持匿名类型,动态创建一次性接口实现就变得很容易了。使用匿名类型,我可以通过以下代码轻松地为 IPerson 提供实现:

DynamicObject dynamic = new DynamicObject();
dynamic.MixWith(new {Name="Me", Age=18});
IPerson person = dynamic.CreateDuck<IPerson>();

// Print the name and age
Console.WriteLine("Name = {0}", person.Name);
Console.WriteLine("Age = {0}", person.Age);

使用 lambda 表达式编写更短的匿名委托

虽然在某些方面,能够使用 lambda 表达式编写更短的匿名委托仅仅是语法糖,但(在我看来)将 lambda 表达式与 LinFu.DynamicObject 结合使用可以使代码更易于阅读。例如,使用前面带有 MathOperation 类型的示例,我可以使用 lambda 表达式重写它,使其看起来像这样:

public delegate int MathOperation(int a, int b);

public class Program
{
   public static void Main()
   {
       // Define the method body
       MathOperation body = (a, b)=> { return a + b; };

       // Create an empty DynamicObject
       // that wraps around a System.Object
       DynamicObject dynamic = new DynamicObject();
       dynamic.AddMethod("Add", body);

       // This will return '2'
       int result = (int)dynamic.Methods["Add"](1, 1);
   }
}

节省空间

起初,节省输入几个字符似乎并没有带来显著的效率提升,但如果 IMath 类定义如下:

public interface IMath
{
   int Add(int a, int b);
   int Subtract(int a, int b);
   int Multiply(int a, int b);
   int Divide(int a, int b);
}

曾经的一个小声明变成了四个,您输入的项目数量实际上增加了 400%。如果您不得不输入更多呢?总之,一旦您输入了十个重复的匿名委托声明,您就会厌倦了。为简单起见,让我们坚持使用上面提到的四个操作。

接下来,我需要重新定义 IMath 类执行的所有操作:

MathOperation add = (a, b) => { return a + b; }
MathOperation subtract = (a, b) => { return a – b; };
MathOperation multiply = (a, b)=> { return a * b; };
// Divide by zero check omitted for brevity
MathOperation divide = (a, b)=> { return a / b; }; 

再次,在 DynamicObject 中,我必须添加新方法并实现接口:

DynamicObject dynamic = new DynamicObject();
dynamic.AddMethod("Add", add);
dynamic.AddMethod("Subtract", subtract);
dynamic.AddMethod("Multiply", multiply);
dynamic.AddMethod("Divide", divide);

IMath math = dynamic.CreateDuck<IMath>();

// Perform all of the operations
Console.WriteLine(math.Add(1, 1));
Console.WriteLine(math.Subtract(1, 1));
Console.WriteLine(math.Multiply(1, 1));
Console.WriteLine(math.Divide(1, 1));

您可以看到,能够使用 lambda 表达式代替完整的匿名委托声明可以节省大量时间和打字。至于上面其余的代码,好吧,如果您已经读到这里,那么我上面提到的所有内容几乎都是样板代码。这几乎就像动态添加方法不再是什么新鲜事一样,但我跑题了。

解析 LINQ 表达式树

随着 LINQ 即将推出,LinFu.DynamicObject 自然而然地成为了一个近乎完美的表达式树访问者。这是一个使用 DynamicObject 的简单(且假设的)访问者示例:

public class ExampleVisitor
{
   ...
   public void DoVisit(Expression expression)
   {
      // Let the DynamicObject decide which method to call
      DynamicObject dynamic = new DynamicObject(this);
      dynamic.Methods["Visit"](expression);
   }
   public virtual void Visit(MethodCallExpression expression)
   {
      // Do something useful here
   }
   public virtual void Visit(BinaryExpression expression)
   {
      // Do something useful here
   }
   ...
}

在此示例中,我使用 DynamicObject 类包装了 ExampleVisitor,并让 DynamicObject 确定 Visit() 方法的哪个重载将处理当前的 LINQ 表达式。一旦客户端代码调用 DoVisit()DynamicObject 就会遍历树,让您对表达式元数据做一些有用的事情。

LinFu.Reflection 功能仍在规划中

愿望清单

现在您已经拥有了 LinFu,以下是我希望实现但坦白说没有足够时间和专业知识来有效解决问题的一些事情。如果您有任何与我的工作类似的项目,我很乐意与您交流这些主题。总之,这是列表:

匿名接口

使用 LinFu.DynamicObject 的一个问题是,除了使用 LooksLike() 方法之外,目前没有办法知道 DynamicObject 实现​​了哪些方法和属性。如果能让 DynamicObject 动态生成一些类型信息,允许它描述其当前的内部状态,那就太好了。这就是匿名接口的概念所在。我可以让每个 DynamicObject 生成一个无名的类型来描述其当前的接口。我可以使用这些动态生成的匿名接口,将它们相互比较并测试它们的相等性。在我弄清楚如何做到这一点之前,要比较两个动态对象并确定它们是否共享相同的方法和属性几乎是不可能的。

差分接口

您是否曾经想过比较两个类,将它们并排查看,并找出它们有哪些共同的属性和方法?例如,假设我想比较一个 ASP.NET 按钮控件和一个标准的 Windows Forms 按钮控件。我想能够立即确定这两个类有哪些共同的成员,以便我可以动态生成一个接口来用于鸭子类型,并将这些共同成员作为新生成接口的一部分。这里的不同之处在于,我希望这些信息在运行时动态生成并在内存中,而不是有什么东西写在无法使用的纸片或白板上。

使用 TypeConverters 进行类型转换

这里的基本思想是修改 DynamicObject,使其足够智能,能够通过检查运行时参数是否与方法签名兼容来确定它是否与方法签名兼容。换句话说,我希望 DynamicObject 足够智能,能够将字符串转换为枚举,反之亦然,并且能够将整数转换为它们的数值,反之亦然。

调用具有开放类型参数的延迟绑定的泛型方法

LinFu.DynamicObject 能够很好地处理带有封闭(已分配)类型参数的泛型方法,但目前没有办法调用方法,如果类具有以下签名:

public class SomeClass
{
   public void DoSomething<T>()
   {
   }
}

在其当前的开发状态下,LinFu.DynamicObject 无法调用 DoSomething<T>() 方法,因为目前没有办法告诉 DynamicObject 我正在寻找一个带有开放泛型类型参数的方法,并且我想使用我分配给它的类型参数来实例化它。这个过程相当复杂,需要我花很多时间来弄清楚如何实现。

下一篇文章预告

在本系列的第三部分,我将向您展示如何使用 LinFu.Delegates 使用 lambda 参数定义闭包。我还将向您展示如何使用 EventBinder 类来处理任何事件,而不管任何特定事件所使用的委托签名是什么。这是一个例子:

MathOperation add = delegate(int a, int b) { return a + b; };

// Note: this closure will only be evaluated
// once the Invoke() method is called
Closure doMath = new Closure(add, new Closure
    (add, new Closure(add, 12, 9), 11), Args.Lambda);

// What's the answer?
// It's 42, of course! :)
int result = (int) doMath.Invoke(20);

……假设我想使用 EventBinder 来绑定到 Windows Forms 的 Button.Click 事件:

Button button = new Button();
CustomDelegate handler = delegate { Console.WriteLine("Button Clicked!"); };
EventBinder.BindToEvent("Click", button, handler);

……如果我真的想变得很花哨,我甚至可以使用 Closures 和 LinFu.DynamicObject

// Derive a new closure from the Add() method that takes a single argument
// and adds '1' to it
Closure lazyMathOperation = new Closure
    (dynamic.Methods["Add"], 1, Args.Lambda);

// This will return '6'
int result = (int) lazyMathOperation.Invoke(5);

下一篇文章之前的最后几句话

现在我已经完成了对 LinFu 动态语言扩展的讨论,我向您,作为一名开发者,提出挑战:利用这种语言的特性,创造出有用的东西。当您找到可以用它做的事情时,我将完成第三部分的其余内容。敬请关注!

历史

  • 2007年10月26日 -- 发布原稿
  • 2007年11月5日 -- 更新文章和下载
  • 2007年11月12日 -- 更新源代码和二进制文件下载
© . All rights reserved.