-1

我了解它们是什么,我只是想知道何时是使用它们的最佳时间。

我的第一个想法是 - 当我们构建一个(静态)实用程序类时,它应该对不同的数据类型执行某些操作。因此,使用泛型方法来避免某个方法的大量重载是一种好习惯吗?请对此发表评论。

我有一个小例子课。这只是为了举例。

public static class Math<T> where T : operator +, operator -
{
    public static T Add(T a1, T a2)
    {
        return a1+a2;
    }

    public static T Subtract(T a1, T a2)
    {
        return a1 - a2;
    }
}
  1. 这会是泛型类和方法的一个很好的用法吗?例如,我希望用最少的代码加减整数、双精度数等。

  2. 为什么编译不出来?我已经尝试过这个以及修改类签名:

    public static class Math<T> where T : struct

我了解我必须指定 Type 参数是引用类型还是值类型。我通过指定必须将 T 约束为值类型来做到这一点,那么为什么我仍然会收到运算符 + 和/或 - 不能应用于 T 的错误(这应该是一个值类型)

4

3 回答 3

4
  1. 不,这不会是一个很好的用途。泛型是在不知道类型的情况下提供类型安全的数据结构。泛型约束允许您指定有关类型的一些语义,例如实现接口、具有默认构造函数或作为类或结构。

    请参阅这些 MSDN 文章:

    .

  2. 它不会编译,因为这些operator +部分不是有效的约束。

    作为值类型不会推断诸如+or之类的运算符-,它仅推断值类型语义(继承object,是值类型,不能为 null,具有默认构造函数)。


通用约束

通用约束帮助编译器从你的T. 无约束的泛型只能被证明是object,因此您只能访问object参数上的成员。

如果您声明:public void Foo<T>() where T : new()

编译器可以证明您的类型具有默认的公共无参数构造函数。这是约束的目的,它强制可以成为泛型一方的类型符合合同。

有各种限制,但正如您发现的那样,存在一些限制。有趣的是,C# 中存在 IL 中不存在的限制,正如 Jon Skeet 在他的Unconstrained Melody库中所探讨的那样,该库将约束暴露enum给 C#。

于 2013-09-06T08:04:21.893 回答
2

正如其他人所写的那样,这operator+不是有效的约束。如果你想要做一些通用数学,你可以使用类似的东西:

public static class Add<T>
{
    public static readonly Func<T, T, T> Do;

    static Add()
    {
        var par1 = Expression.Parameter(typeof(T));
        var par2 = Expression.Parameter(typeof(T));

        var add = Expression.Add(par1, par2);

        Do = Expression.Lambda<Func<T, T, T>>(add, par1, par2).Compile();
    }
}

public static class Math<T>
{
    public static T Add(T a1, T a2)
    {
        return Add<T>.Do(a1, a2);
    }

这将创建并编译一个Expression执行该操作的对象,然后将其缓存在一个通用静态类中。

遗憾的是,使用这种方法,您将失去对编译器的静态检查(您可以执行以下操作:

object res = Math<object>.Add(new object(), new object());

它会正确编译。在运行时它会爆炸。)

一般来说,您不能创建一个要求特定方法(静态或非静态)或特定属性存在的约束(运算符就像静态方法)(有一个例外:new()要求公共无参数构造函数的约束)。您可以要求实现一个接口,或者一个基类是否存在,或者泛型参数是 aclass或 a struct(其中两者必须表示为“引用类型”和“值类型”,并且不只是作为classstruct)。遗憾的是,没有接口IAddable, ISubtractable, ... 即使你构建了它们,int, double... 也不会实现它们,而且更糟糕的是,在 .NET 中你不能有泛型专业化(C++ 的一个技巧,你定义一个泛型Math<T>然后你明确定义特殊的“情况”,比如Math<int>Math<double>等等)

于 2013-09-06T08:08:11.710 回答
0

泛型类的明显用例是数据结构,它可以存储任何类型的数据,而不必将其全部视为object. 您可能一直使用这些 -IList<T>等等IDictionary<K, V>。它可以让您在编写结构时存储不知道类型的内容,同时保持类型安全。诀窍是你也不知道你存储的类型,所以你不能用它做很多事情。

因此泛型约束,它让你说某事是引用类型或值类型,或具有无参数构造函数,或实现接口。当您编写一个必须对参数化类型的实例执行某些操作的泛型类时,这些会很有用。可能看起来没用——为什么不直接使用接口类型作为参数类型并完全避免泛型呢?因为泛型约束可以强制参数符合多个接口 - 您无法在普通参数类型中指定。因此,您可以编写一个函数:

public static void Frobnicate<T>(T thing)
    where T : IList<int>, IDisposable
{
    // ...
}

您也可以在其中粘贴一个基类名称。这比指定具体类型要灵活得多。当然,您可以创建一个继承自的接口,IList<int>IDisposable您不能改造所有可能存在的一次性整数列表来实现它。

您也可以在运行时使用反射来检查事物,但这种事情由编译器 IMO 处理得更好。

于 2013-09-06T08:11:10.640 回答