如何避免在中为不同的数字类型编写重复代码.NET



我正在尝试编写通用的Vector2类型,它将套装float、double等类型并使用算术运算。有没有机会在C#、F#、Nemerle或任何其他或多或少成熟的地方做到这一点。NET语言?

我需要一个的解决方案

  • (1)良好的表现(与我单独写作相同Vector2Float、Vector2Double等类)
  • (2) 这将允许代码看起来很漂亮(我不想为中的每个类发出代码运行时)
  • (3) 并且这将进行尽可能多的编译时检查

由于原因1和3,我不想使用动力学。现在我正在检查F#和Nemerle。

UPD:我希望有很多这种类型的数学代码。但是,如果可能的话,我更愿意将代码放在扩展方法中。

UPD2:"etc"类型包括int(我实际上怀疑我会使用它)和decimal(我认为我可能会使用,但现在不使用)。使用扩展方法只是一个品味问题——如果有充分的理由不这样做,请告诉我。

正如Daniel所提到的,F#有一个名为静态解析类型参数的功能,它超出了正常情况下的功能。NET泛型。诀窍是,如果您将函数标记为inline,F#会自动生成专用代码(有点像C++模板),然后您可以使用F#类型系统的更强大功能来编写通用数学。

例如,如果您编写一个简单的add函数并使其成为inline:

let inline add x y = x + y;;    

类型推断打印以下类型:

val inline add :
x: ^a -> y: ^b ->  ^c
when ( ^a or  ^b) : (static member ( + ) :  ^a *  ^b ->  ^c)

您可以看到,推断的类型相当复杂-它指定了一个成员约束,该约束需要两个参数中的一个来定义+成员(标准.NET类型也支持这一点)。好的是,这可以完全推断,因此您很少需要编写难看的类型定义。

正如评论中所提到的,我写了一篇文章——编写通用数字代码,详细介绍了如何在F#中实现这一点。我认为这在C#中不容易做到,并且在F#中编写的内联函数应该只能从F#中调用(从C#中调用它们本质上是使用动态的)。但是你肯定可以用F#编写你的通用数值计算。

这更直接地解决了您之前的问题。不能将静态成员约束放在结构上,但可以将其放在staticCreate方法上。

不幸的是,仅靠
[<Struct>]
type Vector2D<'a> private (x: 'a, y: 'a) =
static member inline Create<'a  when 'a : (static member (+) : 'a * 'a -> 'a)>(x, y) = Vector2D<'a>(x, y)

C#并不能帮助您实现这一点。在运行时发出结构对您也没有多大帮助,因为您的程序无法静态引用它们。

如果你真的负担不起重复代码的费用,那么据我所知,"离线"代码生成是实现这一点的唯一途径。与其在运行时生成代码,不如使用AssemblyBuilder和朋友在磁盘上创建一个Vector2类型的程序集,或者生成一个要提供给编译器的C#代码字符串。我相信一些本机库包装器采用了这条路线(即OpenTKSharpDX)。如果要将这些类型合并到一个手工编码的库中,则可以使用ilmerge

我假设您一定来自C++背景,在那里使用模板可以很容易地实现这一点。然而,您应该问问自己,是否真的需要基于整型、十进制和其他"奇异"数字类型的Vector2类型。您可能也无法基于特定的Vector2对代码的其余部分进行参数化,因此这项工作可能不值得

查看内联函数和静态解析类型参数。

据我所知,编译时是一个严格的类型,但您不在乎运行时会发生什么。Nemerle语言目前不支持您想要的这种构造。但它支持宏,并允许您编写DSL来生成任意代码。例如,您可以执行一些宏来分析此代码并将其转换为正确的类型。

def vec = vector { [1,2] };

假设我们有或创建了一个类型VectorInt,代码可以转换为

def vec = VectorInt(1,2);

当然,你可以在里面写任何代码,并将其转换为你想要的任何代码:)

运算符可以作为类的常用运算符来实现。Nemerle还允许您定义任何像F#这样的运算符。

使用泛型,这使得也是类型安全的

有关泛型的更多信息:http://msdn.microsoft.com/en-us/library/512aeb7t.aspx

但您也有可用的数据结构,如List和Dictionary

听起来你想要运算符重载,有很多这样的例子。真的没有一个好的方法只允许小数,浮动等等。你唯一能做的就是限制为struct,但这并不是你想要的。

最新更新