限制方法中泛型类型的目的是什么?

15

我很难理解为什么这样做会有益处:(Sample是一个类)

static void PrintResults<T>(T result) where T : Sample

把Sample直接传递到方法中是否更好呢?

 static void PrintResults (Sample result)

6
传递给方法的东西确实受益较小。但对于返回的东西来说,它帮助更大一些,因为消费者不需要进行强制类型转换。 - Joey
3个回答

10

我建议在非泛型语法可行的情况下避免使用通用类型,就像你提供的例子一样。然而,还有其他有用的情况。

例如,以泛型方式指定返回类型:

static T Create<T>() where T: Sample, new()
{
    return new T();
}

// Calling code
Sample sample = Create<Sample>();

替代

static object Create()
{
    return new Sample();
}

// Calling code
Sample sample = (Sample) Create();

您还可以使用模板在类型上设置多个限制条件。例如:

static T Create<T>() where T: IMyInterface, new()
{
    return new T();
}

interface IMyInterface {} 
class MyClass : IMyInterface { }

// Calling code.
MyClass myClass = Create<MyClass>();

这使得可以通用地创建一个实现特定接口并具有泛型构造函数的新类型。此外:

static void DoSomething<T>(T t) where T: IMyInterface1, IMyInterface2
{
    t.MethodOnIMyInterface1();
    t.MethodOnIMyInterface2();
}

interface IMyInterface1 
{
    void MethodOnIMyInterface1();
}     
interface IMyInterface2
{
    void MethodOnIMyInterface2();
}     
class MyClass: IMyInterface1, IMyInterface2 
{ 
    // Method implementations omitted for clarity
}

// Calling code
MyClass myclass'
DoSomething(myclass);  // Note that the compiler infers the type of T.

您可以在单个参数上要求多个接口,而不需要(1)创建实现所有这些接口的新类型和(2)要求参数为该类型。

正如@dcastro在他/她的答案中指出的那样,泛型类型也可以告诉编译器要求类型相同。例如:

static void DoSomething<T>(T t1, T t2) where T: MyType
{
    // ...
}

class MyType {}
class MyType1: MyType {} 
class MyType2: MyType {} 

// Calling code
MyType1 myType1;
MyType2 myType2;
DoSomething<MyType>(myType1, myType2);

编译器要求t1和t2是相同类型但可以是继承MyType的任何类型。这在自动化单元测试框架中非常有用,例如NUnit或MSTest,用于通用的等式和比较检查。


1
关于强制使用相同类型的结尾部分是不正确的。以下任何一种都可以编译成功:DoSomething<MyType>(mytype1, mytype2); DoSomething((MyType)mytype1, mytype2);... - Brandon
@akton 感谢您提供详细的答案。我很感激所有提供的答案!我意识到我没有提供最好的示例,但您成功地回答了问题。再次感谢! - Anonymous
@Brandon 问题不在于代码是否能编译通过。泛型类型 (1) 使代码更易读(虽然在 OP 的情况下不是这样),并且 (2) 将更多的检查从运行时移到编译时(通常是通过替换强制转换)。在特定情况下,这不是最好的例子,但足以回答 OP 的问题。 - akton
2
此外,如果我们对于接口ISamplewhere T : ISample的话,那么就可以让我们在T既是struct又是class的情况下使用该方法。当我们在方法参数result上调用接口ISample的成员时,很酷的一点是,即使T是值类型(结构体),我们在result上调用ISample成员(方法等)时也不会出现装箱。如果结构体具有显式接口实现,我们甚至可以在不装箱结构体的情况下调用它们。 - Jeppe Stig Nielsen

3

大多数回答都提供了关于接口的泛型用途的解释,似乎并未涉及到您实际的问题。

事实上,在您发布的示例中,使用泛型方法没有任何好处。 这实际上是更糟糕的,因为它将导致生成多个相同函数的实现,并在运行时略微增加代码大小。


1
我不认为你提到的“多个实现”适用于他的情况,因为Sample必须是引用类型(实际上他告诉我们它是一个“class”)。对于引用类型,不存在“多个实现”。除此之外,在他的特定场景中可能没有太多好处。我们会得到一个额外的类型 T(这是一个编译时类型,typeof(T)不一定与result.GetType()相同)。该方法可以使用该类型,例如 var config = HelperClass<T>.GetConfig(); 或类似的东西。 - Jeppe Stig Nielsen

2

在空白处,您可以始终使用接口作为参数,使多种类型起作用,因此泛型在这里通常不是很有用。

唯一的例外是对泛型的限制。我的意思不是像“where T:IA,IB”这样的东西,因为也可以通过实现IA和IB的接口来完成这项工作。然而,随着时间的推移,这将变得繁琐,因为您将需要越来越多的接口。因此,让我们看看“特殊约束”类和new关键字。

public void AddNew(List<T> items) where T : new
{
    items.Add(new T());
}

如果方法会改变其参数,那么使用类是有用的,但对于结构体来说则不起作用。

static void IncA<T>(T item) where T: class, IA
{
    item.A++;
}

泛型的真正威力在于方法具有泛型返回类型或像List <T>这样的泛型类。您不想为每个需要的List实现一个新类。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接