有人能告诉我是否有一种方法可以用泛型将泛型类型参数T限制为:
Int16 Int32 Int64 UInt16 UInt32 UInt64
我知道哪里关键字,但不能找到只有这些类型的接口,
喜欢的东西:
static bool IntegerFunction<T>(T value) where T : INumeric
有人能告诉我是否有一种方法可以用泛型将泛型类型参数T限制为:
Int16 Int32 Int64 UInt16 UInt32 UInt64
我知道哪里关键字,但不能找到只有这些类型的接口,
喜欢的东西:
static bool IntegerFunction<T>(T value) where T : INumeric
当前回答
这个问题有点像常见问题,所以我在维基上发布了这个(因为我之前发布过类似的问题,但这是一个较老的问题);无论如何……
你用的是什么版本的。net ?如果你使用的是。net 3.5,那么我在MiscUtil中有一个通用操作符实现(免费等)。
它有T Add<T>(T x, T y)等方法,以及不同类型上的其他算术变体(如DateTime + TimeSpan)。
此外,这适用于所有内置、提升和定制操作符,并缓存委托以获得性能。
这里有一些关于为什么这很棘手的额外背景。
你可能还想知道动态(4.0)也间接地解决了这个问题。
dynamic x = ..., y = ...
dynamic result = x + y; // does what you expect
其他回答
当我试图重载泛型类型的操作符时,这个限制影响了我;由于没有“innumeric”约束,以及stackoverflow上的优秀人员乐于提供的一系列其他原因,操作不能在泛型类型上定义。
我想要的是
public struct Foo<T>
{
public T Value{ get; private set; }
public static Foo<T> operator +(Foo<T> LHS, Foo<T> RHS)
{
return new Foo<T> { Value = LHS.Value + RHS.Value; };
}
}
我已经使用。net4动态运行时类型解决了这个问题。
public struct Foo<T>
{
public T Value { get; private set; }
public static Foo<T> operator +(Foo<T> LHS, Foo<T> RHS)
{
return new Foo<T> { Value = LHS.Value + (dynamic)RHS.Value };
}
}
关于使用动态的两件事是
的性能。所有值类型都被装箱。 运行时错误。你“打败”了编译器,但失去了类型安全。如果泛型类型没有定义操作符,则会在执行期间抛出异常。
这个练习的意义是什么?
正如人们已经指出的,你可以让一个非泛型函数取最大的项,编译器会自动为你转换较小的整型。
static bool IntegerFunction(Int64 value) { }
如果您的函数处于性能关键的路径上(在我看来,这是不太可能的),您可以为所有需要的函数提供重载。
static bool IntegerFunction(Int64 value) { }
...
static bool IntegerFunction(Int16 value) { }
这个约束存在于。net 7中。
查看这篇。net博客文章和实际文档。
从。net 7开始,你可以使用接口如INumber和IFloatingPoint来创建程序,例如:
using System.Numerics;
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;
}
innumber在系统中。数字名称空间。
还有诸如IAdditionOperators和IComparisonOperators这样的接口,因此您可以通用地使用特定的操作符。
我想知道和samjudson一样,为什么只对整数有效?如果是这样的话,你可能想要创建一个helper类或者类似的东西来保存你想要的所有类型。
如果你想要的只是整数,不要使用泛型,那不是泛型;或者更好的是,通过检查其类型来拒绝任何其他类型。
考虑到这个问题的受欢迎程度和这样一个函数背后的兴趣,我很惊讶地看到,还没有涉及T4的答案。
在这个示例代码中,我将演示一个非常简单的示例,说明如何使用强大的模板引擎来完成编译器在幕后使用泛型所做的工作。
你可以简单地为你喜欢的每种类型生成你想要的函数,并相应地使用它(在编译时!),而不是通过循环和牺牲编译时的确定性。
为了做到这一点:
创建一个新的名为GenericNumberMethodTemplate.tt的文本模板文件。 删除自动生成的代码(您将保留大部分代码,但有些代码不需要)。 添加以下片段:
<#@ template language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Core" #>
<# Type[] types = new[] {
typeof(Int16), typeof(Int32), typeof(Int64),
typeof(UInt16), typeof(UInt32), typeof(UInt64)
};
#>
using System;
public static class MaxMath {
<# foreach (var type in types) {
#>
public static <#= type.Name #> Max (<#= type.Name #> val1, <#= type.Name #> val2) {
return val1 > val2 ? val1 : val2;
}
<#
} #>
}
就是这样。你现在完成了。
保存这个文件会自动编译成这个源文件:
using System;
public static class MaxMath {
public static Int16 Max (Int16 val1, Int16 val2) {
return val1 > val2 ? val1 : val2;
}
public static Int32 Max (Int32 val1, Int32 val2) {
return val1 > val2 ? val1 : val2;
}
public static Int64 Max (Int64 val1, Int64 val2) {
return val1 > val2 ? val1 : val2;
}
public static UInt16 Max (UInt16 val1, UInt16 val2) {
return val1 > val2 ? val1 : val2;
}
public static UInt32 Max (UInt32 val1, UInt32 val2) {
return val1 > val2 ? val1 : val2;
}
public static UInt64 Max (UInt64 val1, UInt64 val2) {
return val1 > val2 ? val1 : val2;
}
}
在main方法中,你可以验证你是否具有编译时确定性:
namespace TTTTTest
{
class Program
{
static void Main(string[] args)
{
long val1 = 5L;
long val2 = 10L;
Console.WriteLine(MaxMath.Max(val1, val2));
Console.Read();
}
}
}
我先说一句:不,这并没有违反DRY原则。DRY原则的存在是为了防止人们在多个地方复制代码,从而导致应用程序变得难以维护。
这里的情况完全不同:如果您想要更改,那么您只需更改模板(对于您的所有生成都是一个单一的源代码!),然后就完成了。
为了将它与您自己的自定义定义一起使用,请向生成的代码添加一个名称空间声明(确保它与您将定义自己的实现的名称空间声明相同),并将该类标记为partial。然后,将这些行添加到你的模板文件中,这样它就会被包含在最终的编译中:
<#@ import namespace="TheNameSpaceYouWillUse" #>
<#@ assembly name="$(TargetPath)" #>
说实话:这太酷了。
免责声明:这个示例受到了Kevin Hazzard和Jason Bock, Manning Publications在。net中的元编程的严重影响。