【发布时间】:2019-03-17 17:00:30
【问题描述】:
我正在尝试为 3 维点实现一个简单的结构。出于性能原因,我想把它作为一个结构。我想让它通用(至少对于System.Int32 和System.Double),并定义算术运算符。我打算在混合的 F#/C# 解决方案中使用它。
为了简化代码,将所有内容简化为 1D,这是我开始的:
[<Struct>]
type Point<'T> =
val X: 'T
new(x) = { X = x}
static member inline (+) (p1: Point<'U> when 'U: (static member (+): 'U * 'U -> 'U), p2: Point<'U>): Point<'U> =
Point<_>(p1.X + p2.X)
(+) 操作符上的类型参数需要写成 'U 而不是 'T,否则编译器会抱怨类型约束应该在 Point 的 'T 参数上。
这在 F# 中运行良好,我可以编写
let p1 = Point(2.0)
let sum = p1 + p1
在 C# 中:
var p = new Point<double>(1);
var sum = p + p;
那不编译,说Operator + cannot by applied to operands of type Point<double> and Point<double>
如果我在 dotpeek 中查看已编译的 F# 代码,就会发现 Point<T> 类型上的 + 运算符具有签名 +(Point<???>,Point<???>): Point<???>。我认为这是因为我必须用 'U 来编写类型约束 - 并且可能这也会触发 C# 编译器找不到运算符。
我可以通过定义一个带有操作符的 F# 模块来解决这个问题:
module Ops =
let inline Add(p1, p2: Point<_>) = p1 + p2
有了它,我可以通过 Ops.Add(p1,p2) 在 C# 中进行加法 - 但这显然不像 + 运算符那么容易阅读。
如果我尝试在顶层附加类型约束以进行添加,如下所示:
[<Struct>]
type Point<'T when 'T: (static member (+): 'T * 'T -> 'T)> =
val X: 'T
new(x) = { X = x}
static member inline (+) (p1: Point<'T>, p2: Point<'T>): Point<'T> =
Point<_>(p1.X + p2.X)
然后我在new(x) = { X = x} 收到编译器错误,说This code is not sufficiently generic. The type variable ^T when ^T: (static member...) could not be generalized because it would escape its scope。
有没有什么方法可以让 C# 编译器满意地暴露 + 运算符?
更新:
运算符被标记为inline 的事实对结果没有重大影响:我可以定义
[<Struct>]
type Nothing<'T> =
val X: 'T
new(x) = { X = x}
static member inline (+) (p1: Nothing<'T>, p2: Nothing<'T>): Nothing<'T> =
Nothing<_>(p1.X)
在 C# 中使用这个 + 操作符就好了:
var p1 = new Nothing<double>(1);
var sum = p1 + p1;
【问题讨论】:
标签: c# f# operator-overloading