有什么优雅的方式来操作泛型类型吗?

发布于 2024-11-24 09:38:33 字数 735 浏览 0 评论 0原文

我正在从事一个小型教育项目,我们必须实现一个n维矩阵。根据上下文,此矩阵可以使用我们自己的内置 ComplexNumber 结构,也可以使用 System.Double,对于非常简单的示例,使用整数类型(主要是 <代码>System.Int32)。

由于应用程序的性质,我们不要求实现闪电般的快速性能。

因此,我的第一个想法是实现一个 Matrix,其中 T 需要以某种方式限制为“数字”

这样做的明显问题是,目前语言中无法用已定义的运算符来约束泛型类型 T。另外,我没有看到一种简单的方法来将 T 限制为合理的类型。

我的问题是:

  1. 有人可以请我指出一种优雅的方法来使用泛型类型进行数学运算,不会过多影响性能,并以某种方式使其与内置函数一起使用类型(如果可能的话)。

  2. 如果Eric读过这篇文章,这个功能(通过定义的运算符约束泛型类型)是否会在假设的未来版本的 C# 设计会议中出现,并且是否已经接近将其纳入语言中?

我知道更容易、更好地实现一个复杂矩阵类型并为每个矩阵“子类型”(双精度、整数等)创建围绕它的包装器,并支付复杂矩阵之间转换的性能成本type 以及矩阵元素的类型。这个问题更多的是出于对某人如何实现类似场景的好奇。

I am working in a small educational project where we have to implement a n-dimensional matrix. Depending on the context, this matrix has either to work with our own built-in ComplexNumber struct or with System.Double and for very simple examples, with integral types (mainly System.Int32).

Due to the nature of the application, we are not demanded to implement lightning fast performance.

My first idea was therefore to implement a Matrix<T> where T would somehow need to be restricted to "numbers".

The obvious problem in doing this is that there is no way right now in the language to constraint the generic type T with defined operators. Also I do not see an easy way to restrcit T to reasonable types.

My questions are:

  1. Can somebody please point me in the direction of an elegant way to make mathematical operations with generic types that doesn't compromise performance too much and to somehow make that work with built in types (if possible).

  2. If Eric ever reads this, does this feature (constraining generic types by defined operators) ever come up in hypothetical future versions of C# design meetings and has it ever been close to making it into the language?

I know its easier and better to implement a ComplexMatrix type and create wrappers around it for each matrix "sub-type" (double, integral, etc.) and pay the performance costs of the conversions between our complex type and whichever type the matrix elements happen to be. This question is more out of curiousity on how would someone implement a similar scenario.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

娇纵 2024-12-01 09:38:33

如果埃里克读过这篇文章,

如果您想引起我的注意,请尝试我博客上的联系链接。或者在问题文本中输入我的全名这样我在搜索自己时就能找到我

这个功能(通过定义的运算符约束泛型类型)是否曾在假设的未来版本的 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,等等。

在运行时生成通用代码的世界中,如何有效地做到这一点是一个悬而未决的问题。

我赶紧补充说,这只是一个想法的草图。有很多方法可以实现此类功能。 “接口中的静力学”这一想法比数学具有更广泛的用途,这对我们很有吸引力。如果我们要在这类功能上投入巨额费用,那么最好有一个真正通用的、强大的功能,而不是狭隘地专注于数学的功能。

另一方面,完美是优秀的敌人。最好只专注于数学问题,而不是寻求更昂贵的通用解决方案。

这是一场持续不断的辩论。它肯定出现在每个人的雷达屏幕上,但我预计不会很快出现。语言设计者都在埋头研究异步 CTP 的反馈。

一如既往,埃里克对假设的未宣布的未来产品的假设的未来语言特征的思考仅供娱乐。

If Eric ever reads this,

If you want something brought to my attention, try the contact link on my blog. Or put my full name in the text of the question so that me searching for myself will find me.

does this feature (constraining generic types by defined operators) ever come up in hypothetical future versions of C# design meetings and has it ever been close to making it into the language?

Indeed, this is a frequently requested feature. We've been getting requests for this sort of thing since C# 1.0.

The feature would requires support from the CLR team, not just the language -- it is the sort of feature that we would want to integrate into all our languages, which increases the cost.

The CLR team has expressed interest in features like this, but they also have a lot of competing features that they could be doing, and limited time and effort to implement those features.

There are numerous ways such a feature could be implemented. For example, we could add the ability to specify static methods in interfaces:

interface IAddable<T>
{
    static T operator+(T x, T y);
}

and then

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;
}    

The idea would be that the interface means "a type that implements this interface must have the given static methods". We'd then make int automatically implement IAddable<int>, and so on.

How do do so efficiently in a world with runtime-generated generic code is an open question.

I hasten to add that this is just a sketch of an idea. There are many ways to implement this sort of feature. The "statics in interfaces" idea is one that has broader usage than just mathematics, and that's attractive to us. If we're going to go to the huge expense of this sort of feature, it would be nice to have a really general, powerful feature rather than one narrowly focussed on math.

On the other hand, the perfect is the enemy of the good; it might be better to just concentrate on the math problem and not go for a more expensive general solution.

It's an ongoing debate. It is definitely on everyone's radar screen, but I would not expect it any time soon. The language designers are all heads-down working on going through the feedback from the async CTP.

As always, Eric's musings about hypothetical future language features of hypothetical unannounced future products are for entertainment purposes only.

昔梦 2024-12-01 09:38:33

这取决于您对优雅的看法。如果您的优雅想法是能够编写 a+b 其中 ab 是泛型类型,那将是我的想法优雅,那么这是不能做到的。

遗憾的是,对于此类代码,C# 泛型无法实现 C++ 模板的优雅。

It depends on your idea of what is elegant. If your idea of elegance to being able to write a+b where a and b are of the generic type, and that would be my idea of elegant, then this cannot be done.

Sadly, C# generics cannot achieve the elegance of C++ templates for this type of code.

染火枫林 2024-12-01 09:38:33

实现这个目标的唯一方法有点优雅如下:
让调用者为所需的运算符指定委托并使用这些委托。

例如:

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, ..);

但是,我不知道这种方法的性能......

The only way to achieve this goal somewhat semi elegant is the following:
Let the caller specify a delegate for the needed operators and use these delegates.

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, ..);

However, I have no idea about the performance of this approach...

未蓝澄海的烟 2024-12-01 09:38:33

一种性能不错(但仍然很难看)的解决方法是使用封装您想要的算术行为的结构。

首先定义一个接口:

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;
    }
}

最后将结构作为泛型参数传递到您的类型中:

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

One workaround with decent performance (that's still ugly) is using a struct that encapsulates the arithmetic behavior you want.

You first define an interface:

public interface IArithmetic<T>
{
    T Add(T n1,T n2);
}

Then you implement that interface using a struct:

public struct DoubleArithmetic:IArithmetic<double>
{
    public double Add(double n1,double n2)
    {
        return n1+n2;
    }
}

And finally you pass the struct as generic parameter into your type:

public class Matrix<T,TArithmetic>
  where TArithmetic:struct, IArithmetic<T>
{
  private static readonly TArithmetic arithmetic=new TArithmetic();

  void DoStuff()
  {
    arithmetic.Add(1,2);
  }
}

I haven't benchmarked it, but I suspect it's rather fast since generics get specialized for each value type passed into it. That's why DoubleArithmetic is a struct.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文