C#中泛型类的算术运算符重载

Tri*_*nko 30 c# generics math operator-overloading primitive-types

给出类似的泛型类定义

public class ConstrainedNumber<T> :
    IEquatable<ConstrainedNumber<T>>,
    IEquatable<T>,
    IComparable<ConstrainedNumber<T>>,
    IComparable<T>,
    IComparable where T:struct, IComparable, IComparable<T>, IEquatable<T>
Run Code Online (Sandbox Code Playgroud)

如何为它定义算术运算符?

以下内容无法编译,因为'+'运算符不能应用于类型'T'和'T':

public static T operator +( ConstrainedNumber<T> x, ConstrainedNumber<T> y)
{
    return x._value + y._value;
}
Run Code Online (Sandbox Code Playgroud)

正如您所见,泛型类型'T'受'where'关键字约束,但我需要对具有算术运算符的数字类型(IArithmetic?)进行约束.

'T'将是一个原始数字类型,如int,float等.这些类型是否存在'where'约束?

And*_*are 15

遗憾的是,没有办法将泛型参数约束为整数类型(编辑:我猜"算术类型"可能是一个更好的词,因为这不仅仅与整数有关).

能够做这样的事情会很高兴:

where T : integral // or "arithmetical" depending on how pedantic you are
Run Code Online (Sandbox Code Playgroud)

要么

where T : IArithmetic
Run Code Online (Sandbox Code Playgroud)

我建议您通过我们自己的Marc Gravell和Jon Skeet 阅读Generic Operators.它解释了为什么这是一个如此困难的问题,以及可以采取哪些措施来解决它.

.NET 2.0在.NET世界中引入了泛型,为现有问题的许多优雅解决方案打开了大门.通用约束可用于将类型参数限制为已知接口等,以确保对功能的访问 - 或者对于简单的相等/不等式测试,Comparer.Default和EqualityComparer.Default单例分别实现IComparer和IEqualityComparer(允许我们对元素进行排序)例如,无需了解有关"T"的任何信息.

尽管如此,在运营商方面仍存在很大差距.因为运算符被声明为静态方法,所以没有所有数值类型实现的IMath或类似的等效接口; 事实上,运营商的灵活性会使这项工作变得非常困难.更糟糕的是:原始类型的许多运算符甚至不作为运算符存在; 相反,有直接的IL方法.[强调我的]为了使情况更加复杂,Nullable <>要求"提升运算符"的概念,其中内部"T"描述适用于可空类型的运算符 - 但这是作为语言特征实现的,并且是不是由运行时提供的(使反射更有趣).

  • 那篇文章几乎完全由Marc Gravell编写,事实上 - 他还在MiscUtil中编写了辅助类. (4认同)

Dan*_*fer 15

我认为你能做的最好的事情是IConvertible用作约束并做类似的事情:

 public static operator T +(T x, T y)
    where T: IConvertible
{
    var type = typeof(T);
    if (type == typeof(String) ||
        type == typeof(DateTime)) throw new ArgumentException(String.Format("The type {0} is not supported", type.FullName), "T");

    try { return (T)(Object)(x.ToDouble(NumberFormatInfo.CurrentInfo) + y.ToDouble(NumberFormatInfo.CurrentInfo)); }
    catch(Exception ex) { throw new ApplicationException("The operation failed.", ex); }
}
Run Code Online (Sandbox Code Playgroud)

这不会阻止某人传递String或DateTime,所以你可能想要做一些手动检查 - 但IConvertible应该让你足够接近,并允许你进行操作.

  • 嗯停止删除你的评论:D (9认同)

Ric*_*dOD 12

在C#4.0中,您可以使用动态来绕过此限制.我查看了你的代码,并设法生成了一个工作(虽然削减)版本:

 public class ConstrainedNumber<T> where T : struct, IComparable, IComparable<T>, IEquatable<T>
    {
        private T _value;

        public ConstrainedNumber(T value)
        {
            _value = value;
        }

        public static T operator +(ConstrainedNumber<T> x, ConstrainedNumber<T> y)
        {
            return (dynamic)x._value + y._value;
        }
    }
Run Code Online (Sandbox Code Playgroud)

还有一个小测试程序:

class Program
{
    static void Main(string[] args)
    {
        ConstrainedNumber<int> one = new ConstrainedNumber<int>(10);
        ConstrainedNumber<int> two = new ConstrainedNumber<int>(5);
        var three = one + two;
        Debug.Assert(three == 15);
        Console.ReadLine();
    }
}
Run Code Online (Sandbox Code Playgroud)

请享用!

  • 给未来读者的注意事项,将值类型转换为“动态”会导致装箱分配,如果您的代码对性能至关重要,这可能(将会?)产生影响。 (2认同)

Mar*_*ell 7

C# 11 / .NET 7 给了我们这个:

public class ConstrainedNumber<T> where T : INumber<T>
{
    T _value;
    public static T operator +(ConstrainedNumber<T> x, ConstrainedNumber<T> y)
    {
        return x._value + y._value;
    }
}
Run Code Online (Sandbox Code Playgroud)