在泛型中实现算术?

Tho*_*ith 12 .net c# generics

是否可以在C#泛型中实现基本算法(至少是加法),就像使用C++模板一样?我已经尝试了一段时间来让它们起作用,但是C#不允许你多次声明相同的泛型类型,就像你可以使用模板一样.

广泛的谷歌搜索没有提供答案.

编辑:谢谢,但我正在寻找的是一种在编译时进行算术运算的方法,在泛型类型中嵌入像教会数字这样的东西.这就是为什么我把我做过的文章联系起来的原因.算术泛型类型,而不是算术上的情况下,泛型类型.

Oli*_*bes 7

遗憾的是,您无法对泛型类型使用算术运算

T Add(T a, T b)
{
    return a + b; // compiler error here
}
Run Code Online (Sandbox Code Playgroud)

不会在c#中工作!

但是您可以创建自己的数字类型并重载运算符(算术相等和implicit,explicit).这使您可以以一种非常自然的方式使用它们.但是,您无法使用泛型创建继承层次结构.您将不得不使用非通用基类或接口.

我只是用矢量类型做到了.这是一个缩短的版本:

public class Vector
{
    private const double Eps = 1e-7;

    public Vector(double x, double y)
    {
        _x = x;
        _y = y;
    }

    private double _x;
    public double X
    {
        get { return _x; }
    }

    private double _y;
    public double Y
    {
        get { return _y; }
    }

    public static Vector operator +(Vector a, Vector b)
    {
        return new Vector(a._x + b._x, a._y + b._y);
    }

    public static Vector operator *(double d, Vector v)
    {
        return new Vector(d * v._x, d * v._y);
    }

    public static bool operator ==(Vector a, Vector b)
    {
        if (ReferenceEquals(a, null)) {
            return ReferenceEquals(b, null);
        }
        if (ReferenceEquals(b, null)) {
            return false;
        }
        return Math.Abs(a._x - b._x) < Eps && Math.Abs(a._y - b._y) < Eps;
    }

    public static bool operator !=(Vector a, Vector b)
    {
        return !(a == b);
    }

    public static implicit operator Vector(double[] point)
    {
        return new Vector(point[0], point[1]);
    }

    public static implicit operator Vector(PointF point)
    {
        return new Vector(point.X, point.Y);
    }

    public override int GetHashCode()
    {
        return _x.GetHashCode() ^ _y.GetHashCode();
    }

    public override bool Equals(object obj)
    {
        var other = obj as Vector;
        return other != null && Math.Abs(other._x - _x) < Eps && Math.Abs(other._y - _y) < Eps;
    }

    public override string ToString()
    {
        return String.Format("Vector({0:0.0000}, {1:0.0000})", _x, _y);
    }
}
Run Code Online (Sandbox Code Playgroud)


Dan*_*Dan 7

它来了!

查看这篇.NET 博客文章

从 .NET 6 开始,如果您愿意启用语言预览功能,则可以启用对此的支持。这意味着无法保证该功能将在框架的更高版本 (.NET 7+) 中存在(或以相同的方式工作)。

如果您对此感到满意,请继续阅读以了解如何为您的项目启用此功能。您将能够使用以下接口INumberIFloatingPoint创建以下程序:

using System;

Console.WriteLine(Sum(1, 2, 3, 4, 5));
Console.WriteLine(Sum(10.541, 2.645));
Console.WriteLine(Sum(1.55f, 5, 9.41f, 7));

static T Sum<T>(params T[] numbers) where T : INumber<T>
{
    T result = T.Zero;

    foreach (T item in numbers)
    {
        result += item;
    }

    return result;
}
Run Code Online (Sandbox Code Playgroud)

INumber目前来自System.Runtime.ExperimentalNuGet 包。我的上述示例的项目文件如下所示

<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <EnablePreviewFeatures>true</EnablePreviewFeatures>
        <OutputType>Exe</OutputType>
        <TargetFramework>net6.0</TargetFramework>
        <Nullable>enable</Nullable>
        <LangVersion>preview</LangVersion>
    </PropertyGroup>

    <ItemGroup>
        <PackageReference Include="System.Runtime.Experimental" Version="6.0.0" />
    </ItemGroup>

</Project>
Run Code Online (Sandbox Code Playgroud)

还有诸如IAdditionOperators和之类的接口IComparisonOperators,因此您可以一般地使用特定的运算符。

老实说,我已经等待这个支持很久了。

  • Thom Smith:这应该是 2021 年公认的答案。 (2认同)

Ada*_*rth 5

如果我的回答显得不合时宜,请随时提供更多说明。

至少在 C# 语言中没有对运算符的通用约束。正如 Jon Skeet 用Unconstrained Melody证明的那样,约束实际上可能在 CLR 本身中完全有效。

您可以对约束做的最好的事情是提供接口/自定义类来公开您需要的操作。您将无法提供原语(除非您implicit可能还实现了运算符),但它至少可以让您为数学部分创建通用代码。

通用约束允许编译器根据最小公分母(由约束或缺少的指定)推断可用成员。大多数时候,泛型是不受约束的,因此只给你object语义。


或者,避免使用约束并使用dynamic临时存储通用变量,然后假设(通过鸭子类型)它具有相关运算符:

class Program
{
    static void Main(string[] args)
    {
        var result = Add<int, long, float>(1, 2);
        Console.WriteLine(result); // 3
        Console.WriteLine(result.GetType().FullName); // System.Single
        Console.Read();
    }

    static T3 Add<T1, T2, T3>(T1 left, T2 right)
    {
        dynamic d1 = left;
        dynamic d2 = right;
        return (T3)(d1 + d2);
    }
}
Run Code Online (Sandbox Code Playgroud)

这涉及到 DLR 并且会有一些性能开销(我没有确切的数字),特别是如果您希望计算对性能至关重要。


我不确定你的意思是“多次声明相同的泛型”,这是有效的:

class Tuple<T1, T2> // etc.

var myTuple = new Tuple<int, int>(1, 2);
Run Code Online (Sandbox Code Playgroud)


小智 5

是的,可以通过使用动态类型变量来完成。

例子:

T Add(T value1, T value2)
{           
        dynamic a = value1;
        dynamic b = value2;
        return (a + b);
}
Run Code Online (Sandbox Code Playgroud)

如需更多参考,请点击此处