我什么时候应该或不应该使用泛型类型约束?

发布于 2024-07-30 03:17:38 字数 1294 浏览 9 评论 0原文

我有一个基类:

public abstract class StuffBase
{
    public abstract void DoSomething();
}

和两个派生类

public class Stuff1 : StuffBase
{
    public void DoSomething()
    {
        Console.WriteLine("Stuff 1 did something cool!");
    }
    public Stuff1()
    {
        Console.WriteLine("New stuff 1 reporting for duty!");
    }
}

public class Stuff2 : StuffBase
{
    public void DoSomething()
    {
        Console.WriteLine("Stuff 2 did something cool!");
    }
    public Stuff1()
    {
        Console.WriteLine("New stuff 2 reporting for duty!");
    }
}

好吧,现在说我有一个项目列表:

var items = new List<StuffBase>();
items.Add(new Stuff1());
items.Add(new Stuff2());

我希望它们都调用它们的 DoSomething() 方法。 我可以期望只迭代列表并调用它们的 DoSomething() 方法,所以假设我有一个名为 AllDoSomething() 的方法,它只是迭代列表并完成工作

public static void AllDoSomething(List<StuffBase> items)
{
    items.ForEach(i => i.DoSomething());
}

:以下方法?

public static void AllDoSomething<T>(List<T> items) where T: StuffBase
{
    items.ForEach(i => i.DoSomething());
}

尽管语法不同,但这两种方法实际上都在做同样的事情。

它们只是做同一件事的不同方式吗? 我了解泛型和类型约束,但不明白为什么在这种情况下我会使用一种方法而不是另一种方法。

I've got a base class:

public abstract class StuffBase
{
    public abstract void DoSomething();
}

And two derived classes

public class Stuff1 : StuffBase
{
    public void DoSomething()
    {
        Console.WriteLine("Stuff 1 did something cool!");
    }
    public Stuff1()
    {
        Console.WriteLine("New stuff 1 reporting for duty!");
    }
}

public class Stuff2 : StuffBase
{
    public void DoSomething()
    {
        Console.WriteLine("Stuff 2 did something cool!");
    }
    public Stuff1()
    {
        Console.WriteLine("New stuff 2 reporting for duty!");
    }
}

Okay, now say I've got a list of items:

var items = new List<StuffBase>();
items.Add(new Stuff1());
items.Add(new Stuff2());

and I want them all to call their DoSomething() method. I could expect to just iterate the list and call their DoSomething() method, so let's say I've got a method to do that called AllDoSomething() that just iterates over the list and does the job:

public static void AllDoSomething(List<StuffBase> items)
{
    items.ForEach(i => i.DoSomething());
}

What is the practical difference of the following method?

public static void AllDoSomething<T>(List<T> items) where T: StuffBase
{
    items.ForEach(i => i.DoSomething());
}

Both methods appear in real terms, although being syntactically different, to be doing the same thing.

Are they just different ways of doing the same thing? I understand generics and type constraints but can't see why I would use one way over the other in this instance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

定格我的天空 2024-08-06 03:17:38

这是因为到目前为止,C# 还不支持协方差

更正式地说,在 C# v2.0 中,如果 T 是
U 的子类型,则 T[] 是以下的子类型
U[],但 G 不是 G 的子类型
(其中 G 是任何泛型类型)。 在
类型论术语,我们描述
通过说 C# 数组来实现此行为
类型是“协变”和通用的
类型是“不变的”。

参考:http://blogs.msdn.com /rmbyers/archive/2005/02/16/375079.aspx

如果您有以下方法:

public static void AllDoSomething(List<StuffBase> items)
{
    items.ForEach(i => i.DoSomething());
}

var items = new List<Stuff2>();
x.AllDoSomething(items); //Does not compile

就好像您使用泛型类型约束一样,它会的。

有关协变和逆变的详细信息],请查看 Eric Lippert 的系列帖子


其他值得一读的帖子:

This is because as of yet, C# does not support Covariance.

More formally, in C# v2.0 if T is a
subtype of U, then T[] is a subtype of
U[], but G is not a subtype of G
(where G is any generic type). In
type-theory terminology, we describe
this behavior by saying that C# array
types are “covariant” and generic
types are “invariant”.

Reference: http://blogs.msdn.com/rmbyers/archive/2005/02/16/375079.aspx

If you have the following method :

public static void AllDoSomething(List<StuffBase> items)
{
    items.ForEach(i => i.DoSomething());
}

var items = new List<Stuff2>();
x.AllDoSomething(items); //Does not compile

Where as if you use the generic type constraint, it will.

For more information about Covariance and Contravariance], check out Eric Lippert's series of posts.


Other posts worth reading :

离线来电— 2024-08-06 03:17:38

假设您有一个列表:

List<Stuff1> l = // get from somewhere

现在尝试:

AllDoSomething(l);

使用通用版本,它将被允许。 对于非通用的,则不会。 这就是本质的区别。 Stuff1 列表不是 StuffBase 列表。 但在一般情况下,您不需要它完全是 StuffBase 的列表,因此它更灵活。

您可以通过首先将 Stuff1 列表复制到 StuffBase 列表中来解决此问题,以使其与非通用版本兼容。 但假设您有一个方法:

List<T> TransformList<T>(List<T> input) where T : StuffBase
{
    List<T> output = new List<T>();

    foreach (T item in input)
    {
        // examine item and decide whether to discard it,
        // make new items, whatever
    }

    return output;
}

如果没有泛型,您可以接受 StuffBase 列表,但随后您必须返回 StuffBase 列表。 如果调用者知道这些项确实属于派生类型,则必须使用强制转换。 因此,泛型允许您保留参数的实际类型,并通过方法将其引导到返回类型。

Suppose you had a list:

List<Stuff1> l = // get from somewhere

Now try:

AllDoSomething(l);

With the generic version, it will be allowed. With the non-generic, it won't. That's the essential difference. A list of Stuff1 is not a list of StuffBase. But in the generic case, you don't require it to be exactly a list of StuffBase, so it's more flexible.

You could work around that by first copying your list of Stuff1 into a list of StuffBase, to make it compatible with the non-generic version. But then suppose you had a method:

List<T> TransformList<T>(List<T> input) where T : StuffBase
{
    List<T> output = new List<T>();

    foreach (T item in input)
    {
        // examine item and decide whether to discard it,
        // make new items, whatever
    }

    return output;
}

Without generics, you could accept a list of StuffBase, but you would then have to return a list of StuffBase. The caller would have to use casts if they knew that the items were really of a derived type. So generics allow you to preserve the actual type of an argument and channel it through the method to the return type.

中性美 2024-08-06 03:17:38

在您提供的示例中,没有区别,但请尝试以下操作:

List<Stuff1> items = new List<Stuff1>();
items.Add(new Stuff1());
AllDoSomething(items);
AllDoSomething<StuffBase>(items);

第一个调用运行良好,但第二个调用由于通用协方差而无法编译

In the example you provided there is no difference but try the following:

List<Stuff1> items = new List<Stuff1>();
items.Add(new Stuff1());
AllDoSomething(items);
AllDoSomething<StuffBase>(items);

The first call works well but the second one does not compile because of generic covariance

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文