是否存在將個人通用方法限制爲數字類型的約束?

誰能告訴我泛型是否能夠將泛型類型參數T限制爲: 編程

  • Int16
  • Int32
  • Int64
  • UInt16
  • UInt32
  • UInt64

我知道where關鍵字,可是找不到僅適用於這些類型的接口, 緩存

就像是: ide

static bool IntegerFunction<T>(T value) where T : INumeric

#1樓

這個問題有點像是一個常見問題,因此我將其發佈爲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

#2樓

.NET數字基元類型不共享任何容許它們用於計算的通用接口。 能夠定義本身的接口(例如ISignedWholeNumber ),該接口將執行此類操做,定義包含單個Int16Int32等的結構並實現這些接口,而後具備接受約束到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()方法)。 這種方法將失去與編譯時類型檢查相關的優點,可是仍然能夠避免裝箱操做,而且每種類型僅需「檢查」一次。 以後,與該類型關聯的操做將被委託分派替換。


#3樓

我建立了一些庫功能來解決這些問題:

代替:

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


#4樓

我會使用一種通用的,您能夠處理外部性...

/// <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;
}

#5樓

考慮到這個問題的廣泛性以及這種功能背後的興趣,我很驚訝地看到尚未涉及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)" #>

老實說:這很酷。

免責聲明:該示例受到Manning Publications的Kevin Hazzard和Jason Bock的.NET元編程的嚴重影響。

相關文章
相關標籤/搜索