技术开发 频道

.NET家族新成员:G#语言简介

生成器的继承

  我们上面的例子表明,生成器就是生成的包容器,但是其中还可以包含类能够包含的所有成员(如方法、属性、域、事件等等)。此外可见性和其他修饰符如virtual也可以用于生成。因此,生成器是面向对象的,并且可以彼此继承。这样做的原因和类类似:这允许基生成器定义一个基本的注入行为,并由子生成器定义更多的特殊的行为。

public class Client {  protected string message;  public Client()  {   this.message = “Hello World”;   Messenger(this.message);  }  private void Messenger(string message)  {   onsole.WriteLine(message);  } } public generator Base {  protected virtual generation ChangeIt : target Client.Messenger(*)  {   pre   {    string message = “Hello G#”;   }  post  {   this.message = message;  } } } public generator Sub : Base {  protected override generation ChangeIt : target Client.Messenger(string message)  {   pre   {    base.pre();    message = capture.message;   }   post   {    capture.message = message;    base.Post();   }  } }
  下面给出了发出的Messenger方法。我们来分解一下这些代码。Sub生成器从Base生成器派生而来,并且重写了基类中的方法ChangeIt。基类中使用星号(*)定义了一个目标,它可以被任何参数取代,这意味着它的目标可以是Client类中Messenger的所有重载形式。稍后我们将介绍定义目标的细节。凭经验就可以知道一个基本的规则是,在重写的生成中必须为目标指定更多的特性。在代码的另外一部分中,我们使用了关键字base来访问基生成器的pre和post,因此我们可以决定是在Base生成器发出代码之前还是之后发出Sub生成器的代码。
private void Messenger(string message) {  // Base  string capture.message = "Hello G#";  // Sub  message = capture.message;  Console.WriteLine(message);  // Sub  capture.message = message;  // Base    this.message = capture.message; }
捕获
 
  关键字capture用于引用在同一个生成的作用域中定义的变量,即使这个变量定义在基生成器中。能够访问这些变量的原因是,所有生成的代码都将位于相同的作用域中。在访问被捕获(Capture)的变量时,关键字capture并不是必需的,但这里的Messenger方法使用了同名的变量,在这种情况下,就需要关键字capture来解决混淆问题。变量message定义在Base生成器的ChangeIt生成中,而其目标Messenger方法中也有可能定义同名的参数,因为我们在定义中使用了星号(*)通配符。这种请况很可能发生,因为生成中可以定义局部变量,并且稍后在其目标方法的重载中也可以定义同名的局部变量。如果G#不对其采取行动的话,当目标方法中定义了和生成中的局部变量同名的变量时,就会引发一个编译错误。
 
分节符
 
  为了指出如何发出代码,G#提供了能够通过执行代码来取代发出代码。这通过§符号来实现,该符号称作分节符(Section Sign)。该符号在Times New Roman字体中是这样的:§,而在Courier New字体(译注:原文是Courier字体,这里为了同一代码格式使用了Courier New字体,两者非常相似)中是这样的:§。当在代码中放置了§的时候,其后的代码将被执行,而不是被发出:
pre {  § for(int i = 0; i < 10; i++)  § {     Console.WriteLine(i);  § } }
  绿色高亮的代码在编译期间将被执行而不是被发出。从这个pre块发出的代码是这样的:
Console.WriteLine(0); Console.WriteLine(1); Console.WriteLine(2); Console.WriteLine(3); Console.WriteLine(4); Console.WriteLine(5); Console.WriteLine(6); Console.WriteLine(7); Console.WriteLine(8); Console.WriteLine(9); Console.WriteLine(10);
  注意当这几行代码被发出时,i被它的整数值取代了。G#知道如何注入基本类型如int和float的值,但他无法发出类或其他自定义的复杂类型。如果§后跟了一个方法,该方法的返回值类型必须是基本类型、void或emit,如果是其他类型,则编译过程将会破坏返回的所有东西。我们将在下一节里解释关键字emit。我从来没有见过哪个键盘上有§符号,不过可以通过定义组合快捷键来产生这个符号,我选择Ctrl+l(小写的L)来在Word里输出这个符号,并且在Visual Studio .net中为这个快捷键组合写了一个宏来输出这个符号。

 

0
相关文章