C# 什么时候应该或不应该';我不能使用泛型类型约束吗?

C# 什么时候应该或不应该';我不能使用泛型类型约束吗?,c#,generics,c#-2.0,type-constraints,C#,Generics,C# 2.0,Type Constraints,我有一个基类: public abstract class StuffBase { public abstract void DoSomething(); } 和两个派生类 public class Stuff1 : StuffBase { public void DoSomething() { Console.WriteLine("Stuff 1 did something cool!"); } public Stuff1()

我有一个基类:

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());
var items=newlist();
items.Add(newstuff1());
items.Add(newstuff2());
我希望他们都调用DoSomething()方法。我可以期望只迭代列表并调用他们的DoSomething()方法,所以假设我有一个方法,叫做AllDoSomething(),它只迭代列表并执行任务:

public static void AllDoSomething(List<StuffBase> items)
{
    items.ForEach(i => i.DoSomething());
}
publicstaticvoidalldosomething(列表项)
{
items.ForEach(i=>i.DoSomething());
}
以下方法的实际区别是什么

public static void AllDoSomething<T>(List<T> items) where T: StuffBase
{
    items.ForEach(i => i.DoSomething());
}
public static void AllDoSomething(列表项),其中T:StuffBase
{
items.ForEach(i=>i.DoSomething());
}
这两种方法虽然在语法上有所不同,但实际上是在做同一件事


他们只是做同一件事的不同方式吗?我理解泛型和类型约束,但不明白为什么我会在这个例子中使用一种方法而不是另一种方法。

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

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

参考:

如果您有以下方法:

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

var items = new List<Stuff2>();
x.AllDoSomething(items); //Does not compile
publicstaticvoidalldosomething(列表项)
{
items.ForEach(i=>i.DoSomething());
}
var items=新列表();
x、 所有剂量测定(项目)//不编译
其中,如果您使用泛型类型约束,它将

有关协方差和逆方差的更多信息,请查看


其他值得一读的帖子:


    • 假设您有一个列表:

      List<Stuff1> l = // get from somewhere
      
      对于通用版本,它是允许的。对于非泛型,它不会。这就是本质的区别。
      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;
      }
      
      List TransformList(列表输入),其中T:StuffBase
      {
      列表输出=新列表();
      foreach(输入中的T项)
      {
      //检查项目并决定是否丢弃该项目,
      //做新的东西,随便什么
      }
      返回输出;
      }
      

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

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

      List<Stuff1> items = new List<Stuff1>();
      items.Add(new Stuff1());
      AllDoSomething(items);
      AllDoSomething<StuffBase>(items);
      
      List items=newlist();
      items.Add(newstuff1());
      所有剂量测定(项目);
      所有剂量测定(项目);
      

      第一个调用运行良好,但第二个调用由于泛型协方差而无法编译,因此本质区别在于我的列表不能定义为派生类型的列表,而基类的列表可以包含派生类型?请记住,类型为
      list
      的变量不能有类型为的对象
      List
      分配给它,即使类型为
      B
      的变量可以接受
      D
      。这是因为像List这样的类可以有一个像
      Add
      这样的方法。如果变量是一个
      列表
      ,您就不能将其静默地转换为
      列表
      ,因为这样可以将
      B
      对象添加到它(当它实际上是一个
      列表
      )中。因此,如果我理解正确:在我的示例中,如果没有类型约束,我无法传递List或List的实例,因此必须传递一个List实例,而使用类型约束,我可以?即AllDoSomething(List items),其中T:StuffBase允许我传递List或Listyes实例,因为这样你就可以通过一个简单的通用多态约束“绕过”形式参数的协方差,这非常有意义。我想这不是因为我不理解协方差本身,我只是没有抓住要点。我现在明白了。
      List<Stuff1> items = new List<Stuff1>();
      items.Add(new Stuff1());
      AllDoSomething(items);
      AllDoSomething<StuffBase>(items);