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

时间:2020-03-05 18:44:29  来源:igfitidea点击:

谁能告诉我泛型是否可以将泛型类型参数T限制为:

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

我知道关键字" where",但是找不到仅适用于这些类型的接口,

就像是:

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

解决方案

回答

它们都没有继承任何单一的接口或者基类(其他类也没有继承),因此简单的答案是"否"。

我确实想知道为什么这是一个问题。我们想在IntegerFunction类中只能做整数的事情是什么?

回答

对此没有任何限制。对于任何想使用泛型进行数值计算的人来说,这都是一个现实的问题。

我走得更远,说我们需要

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

甚至

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

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

我们可以将数字包装在其他地方(类似于" INullable <T>"),例如在codeproject上。

我们可以在运行时应用限制(通过反映操作符或者检查类型),但这确实失去了首先拥有泛型的优势。

回答

我们可能最接近的是

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

不知道我们是否可以执行以下操作

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

对于如此具体的内容,为什么列表不仅如此,而且每个类型都有重载,所以它可能会占用更少的内存。

回答

我想知道和samjudson一样,为什么只求整数?如果是这种情况,我们可能想要创建一个帮助器类或者类似的东西来保存所需的所有类型。

如果我们想要的只是整数,请不要使用泛型,即非泛型;或者更好的方法是,通过检查其他类型来拒绝其他任何类型。

回答

不幸的是,在这种情况下,我们只能在where子句中指定struct。我们不能具体指定Int16,Int32等似乎确实很奇怪,但是我敢肯定,在where子句中不允许使用值类型的决定有一些深层的实现原因。

我猜唯一的解决方案是进行运行时检查,不幸的是,该问题阻止了在编译时发现问题。那会像:

static bool IntegerFunction<T>(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...
}

我知道这有点丑陋,但至少提供了所需的约束。

我还将研究此实现可能对性能产生的影响,也许有一种更快的方法。

回答

我认为我们是对仿制药的误解。如果我们尝试执行的操作仅对特定数据类型有用,那么我们就不会执行"泛型"操作。

另外,由于我们只想允许该函数在int数据类型上工作,因此我们不需要为每个特定大小使用单独的函数。只需采用最大特定类型的参数,程序就可以自动将较小的数据类型转换为该参数。 (即在调用时传递Int16将自动转换为Int64)。

如果我们根据传递给函数的int的实际大小执行不同的操作,那么我认为我们应该认真考虑甚至尝试做我们正在做的事情。如果我们必须愚弄语言,则应该多想一些我们想完成的事情,而不是如何去做自己想做的事情。

如果没有其他方法,则可以使用Object类型的参数,然后必须检查参数的类型并采取适当的措施或者引发异常。

回答

练习的目的是什么?

正如人们已经指出的那样,我们可能有一个非泛型函数来处理最大的项,并且编译器会自动为我们转换较小的int。

static bool IntegerFunction(Int64 value) { }

如果功能位于性能至关重要的路径上(IMO不太可能),则可以为所有需要的功能提供重载。

static bool IntegerFunction(Int64 value) { }
...
static bool IntegerFunction(Int16 value) { }

回答

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

不过,我不得不承认,我不知道他如何认为他建议的解决方法会起作用。他的建议是将算术运算推迟到其他通用类中(请阅读访谈!)。这有什么帮助?恕我直言,不多。

回答

这个问题有点像是一个常见问题,因此,我将其发布为Wiki(因为我之前曾发布过类似的文章,但这是一个较旧的问题);反正...

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

它具有诸如" T Add <T>(T x,T y)"之类的方法,以及用于对不同类型进行算术运算的其他变体(例如" DateTime + TimeSpan")。

此外,这适用于所有内置的,提升的和定制的运算符,并缓存代表以提高性能。

这里有一些关于为什么如此棘手的其他背景。

我们可能还想知道dynamic(4.0)排序也间接解决了此问题,即

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

回答

我会使用一种通用的,我们可以处理外部性...

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

回答

当我尝试重载泛型类型的运算符时,这一限制影响了我。由于没有"数字"约束,并且由于许多其他原因,stackoverflow上的好人很乐意提供,因此无法在泛型类型上定义操作。

我想要类似的东西

public struct Foo<T>
{
    public T Value{ get; private set; }

    public static Foo<T> operator +(Foo<T> LHS, Foo<T> RHS)
    {
        return new Foo<T> { Value = LHS.Value + RHS.Value; };
    }
}

我已经使用.net4动态运行时类型解决了此问题。

public struct Foo<T>
{
    public T Value { get; private set; }

    public static Foo<T> operator +(Foo<T> LHS, Foo<T> RHS)
    {
        return new Foo<T> { Value = LHS.Value + (dynamic)RHS.Value };
    }
}

关于使用"动态"的两件事是

  • 表现。所有值类型都装箱。
  • 运行时错误。我们"击败"了编译器,但失去了类型安全性。如果未定义泛型类型,则在执行期间将引发异常。