我们可以使用.NET 7中的新INumber<TSelf>
interface来引用任何数值类型,如下所示:
using System.Numerics;
INumber<int> myNumber = 72;
INumber<float> mySecondNumber = 93.63f;
然而,由于INumber
中的类型约束,我们不能拥有一个可以容纳任何数值类型的泛型引用。下面的代码是无效的:
using System.Numerics;
INumber myNumber = 72;
myNumber = 93.63f;
我如何拥有一个任意数字对象的数组,并调用一个需要INumber<TSelf>
对象的方法。
using System.Numerics;
object[] numbers = new object[] { 1, 2.5, 5, 0x1001, 72 };
for (int i = 0; i < numbers.Length - 1; i++)
{
Console.WriteLine("{0} plus {1} equals {2}", numbers[i], numbers[i + 1], AddNumbers(numbers[i], numbers[i + 1]));
}
static T AddNumbers<T>(T left, T right) where T : INumber<T> => left + right;
4条答案
按热度按时间jyztefdp1#
医生:你不能。
您注意到
INumber<TSelf>
没有实现非泛型类型INumber
,因为这会造成严重破坏。当您必须声明
来保存
INumber<T>
值。您也不能声明,例如类似于
因为您会遇到CS7003:“* 意外使用未绑定的泛型名称 *”
假设存在一个非泛型接口类型
INumber
,它是INumber<TSelf>
的基类型(就像IEnumerable
是IEnumerable<T>
的基类型一样)。要使
INumber
有用,它必须包含操作符之类的东西--但这些操作符也必须是非泛型的--因此,例如,必须存在IAdditionOperators<TSelf,TOther,TResult>
interface(它定义+
操作符)的非泛型版本--它必须将INumber, INumber
作为其参数。现在,假设您有一个类型
UserDefinedNumber : INumber<UserDefinedNumber>
,并且您有那么,您希望
a + b
做什么呢?由于运算符的左边和右边都是
INumber
类型,编译器会在a
(即double
)上使用+
运算符的实现,但由于这是一个内置类型,它不可能有任何逻辑来处理将double
添加到UserDefinedNumber
的操作。p1tboqfb2#
我如何拥有一个任意数字对象的数组,并调用一个需要INumber对象的方法。
问题是数组中所有元素的类型必须相同,因为数组只是一个内存块,第i个元素是内存中的一个位置,位于地址arrayStart + i*(elementSize),如果大小不同,它就不起作用。
因此,对于值类型,这是不可能的(它们可能具有不同的大小),但可以具有对象数组,然后每个元素可以具有任何类型(在值类型的情况下将被装箱)。
所以你需要创建一个对象数组,你可以在里面放float,int,等等.
我也不认为为所有的数字提供一个通用的接口有多大的意义,因为如果你想把float加到long上,你应该怎么做呢?转换成float还是long?把数字转换成最方便的类型会更清楚。
xxe27gdn3#
这是不可能的。
INumber<TSelf>
类型是这样声明的:正如您所看到的,所有接口都使用
TSelf
类型,因此INumber
接口没有支持不同类型之间操作的协定,而只支持同一类型内的操作。因为您有一个混合类型的列表,所以编译器没有机会在运行时检查操作数的实际类型是否是受支持的组合。
bejyjqdl4#
正如@阿威Ha Lee指出的,我们事先并不知道所有的具体实现,所以我们无法涵盖所有的可能性,如果可能性的数量是穷尽的,我们可以为每对可能性实现
IAdditionOperators<TSelf,TOther,TResult>
。然而,如果我们准备作出让步,还有另一种可能性。
引用arithmetic operators (C# reference)的一句话:
当算子为其他整数型别(sbyte、byte、short、ushort或char)时,它们的值会转换成int型别,也就是运算的结果型别。当算子为不同的整数或浮点型别时,它们的值会转换成最接近的包含型别(如果有这种型别存在)。
我们可能都同意这是有道理的。然而,为了论证起见,假设OP中的case对我们来说比舍入误差更重要。例如,接口只存在于我们的库中,我们想用它来修饰几个结构体,这些结构体都应该可以转换为相同的类型(例如
double
)。我们现在可以轻松地将
MyFloat
添加到混合中,而不必触及IMyNumber<>
的以前实现。使用类型别名会使它更加干净。
但后来我开始看到重影。