当前位置:  开发笔记 > 编程语言 > 正文

是否存在将我的泛型方法限制为数字类型的约束?

如何解决《是否存在将我的泛型方法限制为数字类型的约束?》经验,为你挑选了7个好方法。

任何人都可以告诉我是否有一种方法可以使用泛型来限制泛型类型参数T:

Int16

Int32

Int64

UInt16

UInt32

UInt64

我知道的where关键字,但无法找到一个接口只有这些类型,

就像是:

static bool IntegerFunction(T value) where T : INumeric 

Konrad Rudol.. 132

Hejlsberg 在接受 Bruce Eckel 采访时描述了不实现该功能的原因.

不过,我不得不承认,我不知道他认为他提议的解决方法是如何工作的.他的建议是将算术运算推迟到其他一些通用类(阅读采访!).这有什么用?恕我直言,并不多.



1> Konrad Rudol..:

Hejlsberg 在接受 Bruce Eckel 采访时描述了不实现该功能的原因.

不过,我不得不承认,我不知道他认为他提议的解决方法是如何工作的.他的建议是将算术运算推迟到其他一些通用类(阅读采访!).这有什么用?恕我直言,并不多.


顺便说一句,MiscUtil提供了一个完全符合这个要求的通用类; `Operator` /`算`; http://www.yoda.arachsys.com/csharp/miscutil/usage/genericoperators.html
我不同意Heijsberg的短语"所以在某种意义上,C++模板实际上是无类型的,或者是松散类型的.而C#泛型是强类型的." 这真的是推广C#的营销BS.强/弱打字与诊断质量无关.否则:有趣的发现.

2> Jeroen Vanne..:

考虑到这个问题的普及以及这种功能背后的兴趣,我很惊讶地发现还没有涉及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中元编程的严重影响.


与基于策略的解决方案不同,此解决方案为+1,因为它保留了内置整体类型的运行效率.如果多次使用(如数学库中),通过附加(可能是虚拟)方法调用内置CLR运算符(如Add)会严重影响性能.由于整数类型的数量是常量(并且不能继承),因此您只需要重新生成错误修复的代码.

3> Keith..:

对此没有任何限制.对于想要使用泛型进行数值计算的人来说,这是一个真正的问题.

我会更进一步说我们需要

static bool GenericFunction(T value) 
    where T : operators( +, -, /, * )

甚至

static bool GenericFunction(T value) 
    where T : Add, Subtract

不幸的是,你只有接口,基类和关键字struct(必须是值类型),class(必须是引用类型)和new()(必须有默认构造函数)

您可以将数字包装在codeproject上的其他内容(类似于INullable)中.


您可以在运行时应用限制(通过反映运算符或检查类型),但这确实失去了首先使用泛型的优势.


是的 - Jon Skeet不久前曾向我们指出过其他事情(但在今年之后的回应) - 他们是一个聪明的主意,但我仍然喜欢适当的约束支持.
我想知道你是否看过MiscUtil对通用运营商的支持...... http://www.yoda.arachsys.com/csharp/miscutil/usage/genericoperators.html

4> Sergey Shand..:

使用策略的解决方法:

interface INumericPolicy
{
    T Zero();
    T Add(T a, T b);
    // add more functions here, such as multiplication etc.
}

struct NumericPolicies:
    INumericPolicy,
    INumericPolicy
    // add more INumericPolicy<> for different numeric types.
{
    int INumericPolicy.Zero() { return 0; }
    long INumericPolicy.Zero() { return 0; }
    int INumericPolicy.Add(int a, int b) { return a + b; }
    long INumericPolicy.Add(long a, long b) { return a + b; }
    // implement all functions from INumericPolicy<> interfaces.

    public static NumericPolicies Instance = new NumericPolicies();
}

算法:

static class Algorithms
{
    public static T Sum(this P p, params T[] a)
        where P: INumericPolicy
    {
        var r = p.Zero();
        foreach(var i in a)
        {
            r = p.Add(r, i);
        }
        return r;
    }

}

用法:

int i = NumericPolicies.Instance.Sum(1, 2, 3, 4, 5);
long l = NumericPolicies.Instance.Sum(1L, 2, 3, 4, 5);
NumericPolicies.Instance.Sum("www", "") // compile-time error.

该解决方案是编译时安全的.CityLizard Framework提供.NET 4.0的编译版本.该文件是lib/NETFramework4.0/CityLizard.Policy.dll.

它也可以在Nuget中找到:https://www.nuget.org/packages/CityLizard/ .请参见CityLizard.Policy.I结构.



5> Marc Gravell..:

这个问题有点像常见问题解答之一,所以我把它作为wiki发布(因为我以前发过类似的,但这是一个较旧的); 无论如何...

您使用的是什么版本的.NET?如果您使用的是.NET 3.5,那么我在MiscUtil中有一个通用运算符实现(免费等).

这有类似的方法T Add(T x, T y),以及不同类型的算术的其他变体(如DateTime + TimeSpan).

此外,这适用于所有内置,提升和定制的操作员,并缓存代表的性能.

关于为什么这很棘手的一些额外背景在这里.

您可能还想知道dynamic(4.0)排序也间接地解决了这个问题 - 即

dynamic x = ..., y = ...
dynamic result = x + y; // does what you expect



6> ljs..:

不幸的是,您只能在此实例的where子句中指定struct.看起来很奇怪你不能具体指定Int16,Int32等,但我确信在where子句中不允许值类型的决定存在一些深层实现原因.

我想唯一的解决方案是进行运行时检查,不幸的是,这会阻止在编译时拾取问题.那就像: -

static bool IntegerFunction(T value) where T : struct {
  if (typeof(T) != typeof(Int16)  &&
      typeof(T) != typeof(Int32)  &&
      typeof(T) != typeof(Int64)  &&
      typeof(T) != typeof(UInt16) &&
      typeof(T) != typeof(UInt32) &&
      typeof(T) != typeof(UInt64)) {
    throw new ArgumentException(
      string.Format("Type '{0}' is not valid.", typeof(T).ToString()));
  }

  // Rest of code...
}

我知道这有点难看,但至少提供了所需的约束.

我还会研究这种实现可能带来的性能影响,也许还有更快的方法.


+ 1,但是,如果依赖于约束定义的操作,`//其余代码......'可能无法编译.

7> Haacked..:

可能你最接近的是

static bool IntegerFunction(T value) where T: struct

不确定您是否可以执行以下操作

static bool IntegerFunction(T value) where T: struct, IComparable
, IFormattable, IConvertible, IComparable, IEquatable

对于某些特定的东西,为什么不只是为每种类型都有重载,列表是如此之短,它可能会有更少的内存占用.

推荐阅读
刘美娥94662
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有