【问题标题】:How to avoid writing repetitive code for different numeric types in .NET如何避免在 .NET 中为不同的数字类型编写重复代码
【发布时间】:2014-02-25 12:45:55
【问题描述】:

我正在尝试编写适合浮点、双精度等类型并使用算术运算的通用 Vector2 类型。是否有机会使用 C#、F#、Nemerle 或任何其他或多或少成熟的 .NET 语言来实现?

我需要一个解决方案

  • (1) 良好的性能(与我单独编写的相同) Vector2Float、Vector2Double 等类),
  • (2) 这将允许 代码看起来不错(我不想为每个类发出代码 运行时)
  • (3) 并且会尽可能多地进行编译时检查。

出于 1 和 3 的原因,我不想使用动态。现在我正在检查 F# 和 Nemerle。

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

UPD2: 'etc' 类型包括 int(我实际上怀疑我会使用)和 decimal(我想我可能会使用,但不是现在)。使用扩展方法只是个人喜好问题 - 如果有充分的理由不使用,请告知。

【问题讨论】:

  • 这个类型有什么作用?只存储数据?还是它也有方法?这些方法有什么作用? ...这很重要。
  • 什么是“等”类型?
  • 您可能会发现这篇文章很有用:tomasp.net/blog/fsharp-generic-numeric.aspx :-)
  • 顺便说一句:这是一个完全有效的问题,而不是提到的 C# 问题的副本。 F# 有一些东西使它实际上比 C# 更好:-)
  • @ironic: 看看my answer here,应该可以用C#解决这个问题。

标签: c# f# numbers dry nemerle


【解决方案1】:

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

例如,如果你写一个简单的添加函数,并使其成为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 类型支持)这可以完全推断,因此您很少需要编写丑陋的类型定义。

正如 cmets 中提到的,我写了一篇文章 Writing generic numeric code,详细介绍了如何在 F# 中执行此操作。我不认为这可以在 C# 中轻松完成,并且您在 F# 中编写的内联函数只能从 F# 调用(从 C# 调用它们基本上会使用动态)。但是你绝对可以在 F# 中编写你的通用数值计算。

【讨论】:

  • 据我所知,这是唯一的方法。 Boo 支持类似的东西,但 C# / VB.Net 等不支持。
【解决方案2】:

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

[<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)

【讨论】:

    【解决方案3】:

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

    如果您真的无法复制代码,那么据我所知,“离线”代码生成是解决此问题的唯一方法。不要在运行时生成代码,而是使用AssemblyBuilder 和朋友使用您的 Vector2 类型创建磁盘程序集,或者生成一串 C# 代码以提供给编译器。我相信一些本地库包装器采用这种方式(即OpenTKSharpDX)。然后,如果您想将这些类型合并到您的手动编码库之一,则可以使用 ilmerge

    我假设您必须来自 C++ 背景,使用模板可以轻松实现这一点。但是,您应该问自己是否真的需要基于整数、小数和其他“奇异”数字类型的 Vector2 类型。您可能也无法根据特定的 Vector2 参数化其余代码,因此这些努力可能不值得。

    【讨论】:

      【解决方案4】:

      查看内联函数和Statically Resolved Type Parameters

      【讨论】:

      • @ironic - 他们可以,尽管这很不寻常。看我的回答here
      • @kvb,我还是解决不了这个问题:stackoverflow.com/questions/21992377/…
      • @ironic:类型需要是结构吗?如果您不必担心默认构造函数,那么实现约束会更容易一些。
      • @Daniel,我更喜欢它是 struct 有两个原因 - 首先,通常说 struct 快一点,其次,使用 struct 我可以做一些技巧来固定它们的数组并进行一些不安全的处理
      • @ironic - 尝试使用默认构造函数而不是显式构造函数。
      【解决方案5】:

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

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

      假设我们有或创建了一个 VectorInt 类型,代码可以被翻译成

      def vec = VectorInt(1,2);
      

      当然你可以在里面写任何代码,然后把它转换成你想要的任何代码:)

      操作符可以作为类的普通操作符来实现。 Nemerle 还允许您定义任何运算符,例如 F#。

      【讨论】:

        【解决方案6】:

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

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

        但您也有可用的数据结构,例如 ListDictionary

        听起来你想要operator overloading,这方面的例子很多。没有真正的好方法只允许贴花,浮动等。您唯一能做的就是限制为 struct,但这并不是您想要的。

        【讨论】:

        • 除非我弄错了,否则不可能将泛型参数仅限于数字类型。您能否扩展一个考虑到这一点的解决方案(考虑到您在此处所做的只是链接到泛型泛型教程)。
        • 对不起,不够具体 - 我需要一个解决方案,让编译器知道我的类型参数 'T' 具有静态操作 +、-、/、* 并生成相应的 IL 指令。
        猜你喜欢
        • 2012-05-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-09-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多