誰能告訴我泛型是否能夠將泛型類型參數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元編程的嚴重影響。