C# 泛型类-何时使用以及为什么使用

C# 泛型类-何时使用以及为什么使用,c#,generics,C#,Generics,我明白它们是什么,我只是想知道什么时候是使用它们的最佳时机 我的第一个想法是——当我们构建一个静态实用程序类时,它应该对不同的数据类型执行某些操作。 因此,使用泛型方法来避免某个方法的大量重载是一个很好的实践?请对此发表评论 我有一个小例子课。这只是为了举个例子 public static class Math<T> where T : operator +, operator - { public static T Add(T a1, T a2) {

我明白它们是什么,我只是想知道什么时候是使用它们的最佳时机

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

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

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;
    }
}
这是泛型类和方法的一个很好的用法吗,例如我希望加和减整数,double。。等用最少的代码量

为什么这个不能编译?我尝试过这一点,并修改了类签名:

公共静态类数学,其中T:struct

我知道我必须指定类型参数是引用类型还是值类型。 我通过指定T必须被约束为值类型来实现这一点,那么为什么我仍然得到这样的错误:运算符+和/或-不能应用于T,而T应该是值类型

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

请参阅以下MSDN文章:

它不会编译,因为运算符+部分不是有效的约束

作为值类型不会推断运算符,例如+或-,它只推断值类型语义继承对象,是值类型,不能为null,具有默认构造函数

一般约束

泛型约束帮助编译器从T中获得更多。无约束泛型只能被证明是对象,因此您只能访问参数上的对象成员

如果您声明:public void Foo其中T:new

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

有各种各样的限制,但正如您所发现的,也有一些限制。有趣的是,正如Jon Skeet在其向C公开枚举约束的库中所探索的那样,C中存在IL中不存在的限制。

正如其他人所写,运算符+不是有效的约束。如果你想做一些普通数学,你可以使用以下方法:

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);
    }
object res = Math<object>.Add(new object(), new object());
它将正确编译。在运行时,它会爆炸


通常,您不能使要求特定方法静态或非静态或特定属性存在的约束运算符与静态方法类似,只有一个例外:要求公共无参数构造函数的新约束。您可以要求实现一个接口,或者要求存在一个基类,或者要求泛型参数是一个类或一个结构,其中这两个参数必须是引用类型和值类型,而不仅仅是类和结构。可悲的是,没有可编辑的、不可编辑的接口。。。即使你建立了它们,int,double。。。不会实现它们,并且使它更糟,在.NET中,不能使用泛型专门化,这是C++中的一个技巧,在这里定义了一个通用数学,然后显式地定义特殊情况,比如马思。数学等

泛型类的明显用例是数据结构,它可以存储任何类型的数据,而不必将其全部视为对象的实例。您可能一直在使用它们—IList、IDictionary等。它允许您在编写结构时将不知道类型的内容存储在其中,同时保持类型安全性。诀窍在于,您对存储的类型也一无所知,因此无法对其进行太多处理

因此,泛型约束允许您表示某个对象是引用类型或值类型,或具有无参数构造函数,或实现接口。当您编写泛型类时,这些函数非常有用,泛型类必须处理参数化类型的实例。可能看起来没用-为什么不直接使用接口类型作为参数类型并完全避免泛型?因为泛型约束可以强制一个参数符合多个接口,这是在普通参数类型中无法指定的。因此,您可以编写一个函数:

public static void Frobnicate<T>(T thing)
    where T : IList<int>, IDisposable
{
    // ...
}
您也可以在其中粘贴一个基类名称。这比指定具体类型灵活得多。当然,您可以创建一个从IList和IDisposable继承的接口,但是您不能改装所有可能用于实现它的一次性整数列表


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

我认为您不能在c中使用运算符+作为泛型约束。查看这篇博文:不幸的是,这个特定的例子不是有效的C语言,也不起作用。
泛型不支持这一点。实现这一点的一种方法是,尽管我怀疑我是否会使用它,但只需将两个参数作为dynamic和duck类型的运算符传递,这样就行了。您可以只将运算符应用于那些定义了用法的类型。仅仅因为T是值类型并不意味着编译器知道T+T是如何工作的。如果定义一个包含10个字段的结构,每个字段的类型不同,您希望如何添加它?通过添加所有字段的值?只加两个?等等