谁能告诉我泛型是否能够将泛型类型参数T
限制为: 编程
Int16
Int32
Int64
UInt16
UInt32
UInt64
我知道where
关键字,可是找不到仅适用于这些类型的接口, 缓存
就像是: ide
static bool IntegerFunction<T>(T value) where T : INumeric
这个问题有点像是一个常见问题,因此我将其发布为Wiki(由于我以前发布过相似的文章,但这是一个较旧的问题); 不管如何... 函数
您正在使用什么版本的.NET? 若是您使用的是.NET 3.5,那么我在MiscUtil (免费等)中有一个通用的运算符实现 。 性能
它具备T Add<T>(T x, T y)
,以及用于不一样类型(如DateTime + TimeSpan
)的其余算术变体。 测试
此外,这适用于全部内置,提高和定制的运算符,并缓存表明以提升性能。 编码
为何这是棘手的一些其余背景在这里 。 spa
您可能还想知道dynamic
(4.0)排序也间接解决了此问题-即 code
dynamic x = ..., y = ... dynamic result = x + y; // does what you expect
.NET数字基元类型不共享任何容许它们用于计算的通用接口。 能够定义本身的接口(例如ISignedWholeNumber
),该接口将执行此类操做,定义包含单个Int16
, Int32
等的结构并实现这些接口,而后具备接受约束到ISignedWholeNumber
泛型类型的方法,但具备将数值转换为您的结构类型可能会很麻烦。 排序
一种替代方法是使用静态属性bool Available {get;};
定义静态类Int64Converter<T>
bool Available {get;};
以及Int64 GetInt64(T value)
, T FromInt64(Int64 value)
, bool TryStoreInt64(Int64 value, ref T dest)
静态委托。 该类的构造函数可使用硬编码来加载已知类型的委托,并可使用Reflection来测试T
类型是否使用正确的名称和签名来实现方法(若是它像包含Int64
并表示数字的结构那样,但具备自定义的ToString()
方法)。 这种方法将失去与编译时类型检查相关的优点,可是仍然能够避免装箱操做,而且每种类型仅需“检查”一次。 以后,与该类型关联的操做将被委托分派替换。
我建立了一些库功能来解决这些问题:
代替:
public T DifficultCalculation<T>(T a, T b) { T result = a * b + a; // <== WILL NOT COMPILE! return result; } Console.WriteLine(DifficultCalculation(2, 3)); // Should result in 8.
您能够这样写:
public T DifficultCalculation<T>(Number<T> a, Number<T> b) { Number<T> result = a * b + a; return (T)result; } Console.WriteLine(DifficultCalculation(2, 3)); // Results in 8.
您能够在此处找到源代码: https : //codereview.stackexchange.com/questions/26022/improvement-requested-for-generic-calculator-and-generic-number
我会使用一种通用的,您能够处理外部性...
/// <summary> /// Generic object copy of the same type /// </summary> /// <typeparam name="T">The type of object to copy</typeparam> /// <param name="ObjectSource">The source object to copy</param> public T CopyObject<T>(T ObjectSource) { T NewObject = System.Activator.CreateInstance<T>(); foreach (PropertyInfo p in ObjectSource.GetType().GetProperties()) NewObject.GetType().GetProperty(p.Name).SetValue(NewObject, p.GetValue(ObjectSource, null), null); return NewObject; }
考虑到这个问题的广泛性以及这种功能背后的兴趣,我很惊讶地看到尚未涉及T4的答案。
在这个示例代码中,我将演示一个很是简单的示例,说明如何使用功能强大的模板引擎来完成编译器在泛型背后的工做。
您无需花钱,也没必要牺牲编译时的肯定性,您只需为所需的每种类型生成所需的函数,而后相应地使用它便可(在编译时!)。
为此:
<#@ 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)" #>
老实说:这很酷。
免责声明:该示例受到Manning Publications的Kevin Hazzard和Jason Bock的.NET元编程的严重影响。