我正在一个小型教育项目中工作,我们必须实现 n - 维矩阵。根据上下文,此矩阵可以使用我们自己的内置ComplexNumber
结构或System.Double
,也可以使用整数类型(主要是System.Int32
)。
由于应用程序的性质,我们不需要实现闪电般快速的性能。
因此,我的第一个想法是实现一个Matrix<T>
,其中T
必须以某种方式限制为“数字”。
这样做的一个明显问题是,语言中没有办法用定义的运算符约束泛型类型T
。另外,我没有看到将T
重新命名为合理类型的简单方法。
我的问题是:
有人可以指点一下优雅方式的方向,使用不会过多影响性能的泛型类型进行数学运算,并以某种方式使其与内置类型一起工作(如果可能的话)。
如果 Eric 读过这篇文章,那么这个功能(通过定义的运算符限制泛型类型)是否会出现在C#设计会议的假设未来版本中,并且它已经接近于制作它进入语言?
我知道实现ComplexMatrix
类型更简单,更好,并为每个矩阵“子类型”(双精度,整数等)创建包装器,并支付我们复杂的转换之间的性能成本类型和矩阵元素恰好是哪种类型。这个问题更多的是出于对某人如何实施类似情景的好奇心。
答案 0 :(得分:17)
如果Eric读过这篇文章,
如果您想要引起我的注意,可以尝试我博客上的联系链接。或者将我的全名放在问题so that me searching for myself will find me的文本中。
这个功能(通过定义的运算符约束泛型类型)是否会出现在C#设计会议的假设未来版本中,并且它是否已经接近将其变为语言?
实际上,这是一个经常被要求的功能。自从C#1.0以来,我们一直在收到这类东西的请求。
该功能需要CLR团队的支持,而不仅仅是语言 - 这是我们希望集成到所有语言中的一种功能,这会增加成本。
CLR团队已表示对此类功能感兴趣,但他们也有许多竞争功能,以及实施这些功能所需的时间和精力。
有很多方法可以实现这样的功能。例如,我们可以添加在接口中指定静态方法的功能:
interface IAddable<T>
{
static T operator+(T x, T y);
}
然后
static T Sum<T>(IEnumerable<T> seq) where T : IAddable<T>
{
T sum = default(T);
foreach(T item in seq) sum = sum + item;
return sum;
}
这个想法是接口意味着“实现此接口的类型必须具有给定的静态方法”。然后我们让int自动实现IAddable<int>
,依此类推。
如何在运行时生成的通用代码的世界中有效地 这是一个悬而未决的问题。
我赶紧补充一点,这只是一个想法的草图。有很多方法可以实现这种功能。 “界面中的静态”思想是一种比数学更广泛使用的思想,这对我们很有吸引力。如果我们要付出这种功能的巨大代价,那么拥有一个非常通用,强大的功能而不是一个专注于数学的功能将会很棒。
另一方面,完美是善的敌人;最好只关注数学问题而不是寻求更昂贵的通用解决方案。
这是一场持续的辩论。它肯定在每个人的雷达屏幕上,但我不希望它很快。语言设计人员都在努力完成异步CTP的反馈。
一如既往,Eric对假设未经宣布的未来产品假设的未来语言特征的思考只是为了娱乐目的。
答案 1 :(得分:2)
实现这一目标的唯一方法有点半优雅如下: 让调用者为所需的运算符指定委托并使用这些委托。
e.g:
class Matrix<T>
{
Func<T, T, T> _add;
Func<T, T, T> _subtract;
// ...
public Matrix(Func<T, T, T> add, Func<T, T, T> subtract, ...)
{
_add = add;
_subtract = subtract;
// ...
}
}
var m = new Matrix<int>((a,b) => a+b, (a,b) => a-b, ...);
// Assuming that ComplexNumber has two static methods Add and Subtract
var m = new Matrix<ComplexNumber>(ComplexNumber.Add, ComplexNumber.Subtract, ..);
但是,我不知道这种方法的表现......
答案 2 :(得分:2)
这取决于你对什么是优雅的想法。如果你能够写出a+b
a
b
和{{1}}属于通用类型的优雅想法,那将是我的优雅想法,那么就无法做到这一点。
可悲的是,C#泛型无法为这类代码实现C ++模板的优雅。
答案 3 :(得分:2)
一个具有良好性能的解决方法(仍然很丑陋)是使用一个封装所需算术行为的结构。
首先定义一个界面:
public interface IArithmetic<T>
{
T Add(T n1,T n2);
}
然后使用struct
:
public struct DoubleArithmetic:IArithmetic<double>
{
public double Add(double n1,double n2)
{
return n1+n2;
}
}
最后,将struct作为泛型参数传递给您的类型:
public class Matrix<T,TArithmetic>
where TArithmetic:struct, IArithmetic<T>
{
private static readonly TArithmetic arithmetic=new TArithmetic();
void DoStuff()
{
arithmetic.Add(1,2);
}
}
我没有对它进行基准测试,但我怀疑它相当快,因为泛型专门针对传递给它的每种值类型。这就是为什么DoubleArithmetic
是struct
。