是否存在将个人通用方法限制为数字类型的约束?

谁能告诉我泛型是否能够将泛型类型参数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元编程的严重影响。

相关文章
相关标签/搜索