如何使用泛型变量执行数学运算

本文关键字:运算 执行数 变量 何使用 泛型 | 更新日期: 2023-09-27 17:51:21

我正在做一个项目,它需要整数的字符串表示,但使用。net框架不支持的奇数基数(据我所知)——比如base36、62、64等。
我们决定编写一个完整的字符串转换系统,它可以处理任何数字进制,因为它是一个足够简单的操作。

之后,我们想要创建一个自定义IFormatProvider/ICustomFormatter,以使其更易于使用。
但是首先,我们想要解决转换过程本身,以一些执行转换并返回一些基本结果的静态方法的形式。
一旦我们完成了这个工作,我们将把IFormatProvider包装器放在适当的位置。

我已经有一段时间没有使用c#泛型了,我不记得如何让编译器对这个操作满意了。
我更喜欢创建一个私有的静态泛型ConvertInteger方法,然后我可以从公共静态Convert方法中调用它来帮助强制强类型,而不会使它使用起来混乱。这样设置的一个主要原因是为了避免在转换有符号值和无符号值时出现符号位问题。
现在我们有longulong的公共静态convert方法来帮助避免有符号值和无符号值之间的转换问题。
将来,假设我们可以让私有静态泛型方法工作,我们希望扩展公共方法,将int、uint、short、ushort、byte和sbyte作为显式实现,以帮助在大量不同整数大小的值上执行此方法时提高性能。
这就是泛型方法设计派上用场的地方,所以我们不必一遍又一遍地重复完全相同的代码(也使测试和调试更简单)。

我遇到的问题是,编译器不允许我使用泛型类型执行比较或数学操作,因为它不知道如何在提供泛型类型参数值之前执行操作。
(作为一名c++开发人员,这确实让我很恼火,因为c++解决这个问题的方法是,在
之后提供类型参数值之前,不要试图解释泛型代码。)

我需要做什么来满足这个泛型方法设计的编译器?
我提供了下面的代码,注释指出了具体的编译错误。

public static class NumericStringConverter
{
    private static readonly string[] StandardDigits = new string[]
    {
        "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
        "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
        "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
        "_", "-"
    };

    private static string ConvertInteger<T>(T Value, T Base)
    {
        if (Base < 2) // error here: "Operator '<' cannot be applied to operands of type 'T' and 'int'"
            throw new ArgumentOutOfRangeException("Base", Base, "The NumericStringConverter.Convert(Value, Base) method was called, with the Base parameter set to a value less than 2.");
        if (Base > 64) // error here: "Operator '>' cannot be applied to operands of type 'T' and 'int'"
            throw new ArgumentOutOfRangeException("Base", Base, "The NumericStringConverter.Convert(Value, Base) method was called, with the Base parameter set to a value greater than 64.");
        if (Value == 0) // error here: "Operator '==' cannot be applied to operands of type 'T' and 'int'"
            return StandardDigits[0];
        string strResult = "";
        bool IsValueNegative = (Value < 0); // error here: "Operator '<' cannot be applied to operands of type 'T' and 'int'"
        while (Value != 0) // error here: "Operator '!=' cannot be applied to operands of type 'T' and 'int'"
        {
            strResult = strResult.Insert(0, StandardDigits[Math.Abs(Value % Base)]); // error here: "Operator '%' cannot be applied to operands of type 'T' and 'T'"
            Value /= Base; // error here: "Operator '/=' cannot be applied to operands of type 'T' and 'T'"
        }
        if (IsValueNegative)
            strResult = strResult.Insert(0, "-");
        return strResult;
    }

    public static string Convert(long Value, long Base)
    {
        return ConvertInteger<long>(Value, Base);
    }
    public static string Convert(ulong Value, ulong Base)
    {
        return ConvertInteger<ulong>(Value, Base);
    }
    public static T Convert<T>(string Value, T Base)
    {
        return default(T); // TODO: convert a string back into an integer value.
    }
}

如何使用泛型变量执行数学运算

由于您不能定义只允许整数类型的泛型约束,因此我将只使用适用于longs的方法和适用于int的重载方法:

private static string ConvertInteger(long Value, byte Base)
{
    if (Base < 2)
        throw new ArgumentOutOfRangeException("Base", Base, "The NumericStringConverter.Convert(Value, Base) method was called, with the Base parameter set to a value less than 2.");
    if (Base > 64) 
        throw new ArgumentOutOfRangeException("Base", Base, "The NumericStringConverter.Convert(Value, Base) method was called, with the Base parameter set to a value greater than 64.");
    if (Value == 0) 
        return StandardDigits[0];
    string strResult = "";
    bool IsValueNegative = (Value < 0); 
    while (Value != 0) 
    {
        strResult = strResult.Insert(0, StandardDigits[Math.Abs(Value % Base)]); 
        Value /= Base; 
    }
    if (IsValueNegative)
        strResult = strResult.Insert(0, "-");
    return strResult;
}
public static string Convert(int Value, byte Base)
{
    return ConvertInteger((long)Value, Base);
}
public static string Convert(ulong Value, byte Base)
{
    return ConvertInteger((long)Value, Base); // check for overflow?
}

c# 10和。net 6: 2021年11月以后的解决方案

好消息:在。net 6和c# 10中有了解决方案,参见https://devblogs.microsoft.com/dotnet/preview-features-in-net-6-generic-math/#generic-math

为了执行数学运算,您可以接受任何数字类型INumber<T>并指定所需的操作符,例如:

public T Adding<T>(T a, T b)
   where T : INumber<T>
   where T : IAdditionOperators<T, T, T>
{
   return a + b;
}

注:在我回答的时候,这个功能只是一个预览。微软将在。net 6的最终版本中保留这个功能,因为他们仍然希望允许漏洞出现。要使用该特性,必须在项目配置中启用预览特性:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <EnablePreviewFeatures>true</EnablePreviewFeatures>
    <LangVersion>preview</LangVersion>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="System.Runtime.Experimental" Version="6.0.0-preview.7.21377.19" />
  </ItemGroup>
</Project>