15.2.3 在 C# 中创建简单的行为
我们将从创建行为的几个基本函数开始。一旦有了一个好的方式进行可视化行为以后,返回到这个主题,并添加有趣的构造。
创建行为最简单的方法,是取一个我们可以直接使用的函数,作为行为的底层表示。这将是一个内部方法,用于其他基元的实现。因为我们要对象的创建打包到一个方法中,可以在非泛型类中,创建一个泛型方法,并使用 C# 的类型推断,使调用代码更简单。这类似于我们以前的实现的辅助函数,比如 Option.Some。下面是代码这样创建的方法。
Listing 15.4 Creating behavior from a function (C#)
internal static class Behavior {
internal static Behavior<T> Create<T>(Func<BehaviorContext, T> f) {
return new Behavior<T>(f);
}
}
清单 15.4 创建了不可变类 Behavior<T> 的新实例。我们会看到,可以直接从用户的代码创建大部分的行为,而不使用这个方法,因此,可以将其标记为内部。这意味着,内部表示的行为可以保持完全隐藏。
接下来,我们可以使用 Behavior.Create 方法,来创建几个基元构造,提供给用户。在 C# 中,会把它们公开为静态类中的静态方法和属性。可以在清单 15.5 中看到这个类。
Listing 15.5 Primitive behaviors (C#)
public static class Time {
public static Behavior<float> Current {
get { return Behavior.Create(ctx => ctx.Time); }
}
public static Behavior<float> Wiggle {
get { return Behavior.Create(ctx =>
(float)Math.Sin(ctx.Time * Math.PI)); }
}
public static Behavior<T> Forever<T>(T v) {
return Behavior.Create(ctx => v);
}
public static Behavior<float> Forever(this float v) {
return Behavior.Create(ctx => v);
}
}
前三个构造非常简单。Current 属性返回表示当前时间的行为,名为 Wiggle 的属性,计算时间的正弦函数,这在动画中非常有用,正弦可以用于创建圆周运动,我们稍后会使用它来创建旋转图形。Forever 方法创建的行为,始终具有相同的值,这是作为参数值指定的,这个方法是泛型,所以,可以用它来创建任何类型的常量行为。这个方法的非泛型重载,使得在创建浮点数常量行为时,使用更好的语法成为可能。
我们稍后将使用这些行为创建动画,但是,可视化需要另外的方法:我们可以用明显的方法,绘制按时间变化的数值的图。图 15.2 显示了我们刚刚实现的三种基元行为。在本书的网站上有绘制屏幕快照函数的源代码,因此,可以用它来实验自己的行为。
清单 15.5 中的最后构造是 C# float 类型的扩展方法。C# 语法对数字的文本,可以直接调用方法,所以,我们可以使用这个方法来写表达式,比如 0.5f.Forever()。语法很简单,使我们的库看上去有点像 DSL。在后面的示例中,此构造会很常用;直接调用 Time.Forever 方法,使代码可读性降低。
我们为什么不创建一个泛型方法,它也是扩展方法?那也行,但我们希望为每一个 .NET 类型添加 Forever 方法。
图 15.2 在前两秒钟的基元行为。current 值的范围从 0 到 2,wiggle 在 +1 到 –1 之间振荡。constant 行为的值是 1.5。
这看起来像大材小用,因为,我们不需要为大多数的 .NET 类型创建行为。另一方面,这个扩展方法对于浮点数有意义,因为,我们需要经常从浮点数创建常量行为。
现在,我们知道想要实现什么行为,同样的事情在在 F# 中很容易做到。随后,我们将在 F# Interactive 中试验,在本章后面学习如何使用。