在C#Generics上使用约束有什么好处

时间:2010-05-22 00:09:43

标签: c# generics

有人可以告诉我以下

之间的区别
public class CarCollection<T>:List<T> where T:Car{}

public class CarCollection:List<Car>{}

对我来说,他们似乎做同样的事情,创建“Car”对象的类型安全集合。

7 个答案:

答案 0 :(得分:7)

当您需要有关泛型类型参数的一些信息时,约束很好。通过约束类型,您不仅限制了有效类型参数的集合,而且还使您能够根据该类型的公共元素做出编译时决策。

在您的具体示例中,通用约束不提供任何好处,但在其他情况下它们可以。考虑这个例子:

T foo<T>() 
{
    return null;
}

这个方法无法编译,因为编译器知道(当我编写方法时我也应该知道)泛型类型参数可能是一个无法设置为null的值类型。通过添加约束,我可以编译此代码:

T foo<T>() 
    where T : class
{
    return null;
}

我现在已经将有效的泛型类型参数集限制为只有类。由于编译器现在知道T必须是一个类,它允许我返回null,因为不存在无法返回null的情况。虽然这不是一个非常有用的例子,但我相信你可以推断这有用的方法。

答案 1 :(得分:4)

如果实现将执行比较的泛型,则where子句是必需的。例如:

public class Foo<T> where T : IComparable<T>
{
    public static void Bar(T blah, T bloo)
    {
        if(blah.CompareTo(bloo) < 0)    //needs the constraint
            Console.WriteLine("blee!");
    }
}

这是一个具体示例,但它说明了where子句标识您的泛型类型将遵循某种类型,从而能够利用功能的概念。

答案 2 :(得分:1)

我认为第二种格式具有误导性:“Car”是一个模板参数,而不是对你可能定义的“Car”类的引用。

第一种格式允许您在模板类中的T实例上调用Car类的成员(方法和属性)。

class Car
{
  public void drive() {}
}

public class CarCollection<T>:List<T> where T:Car
{
  List<T> list;

  void driveCars()
  {
    foreach (T car in list)
    {
      //know that T is Car
      car.drive();
    }
  }
} 

public class CarCollection<Car>:List<Car>
{
  List<Car> list;

  void driveCars()
  {
    foreach (Car car in list)
    {
      //compiler error: no relation between the 'Car' template parameter
      //and the 'Car' class
      car.drive();
      }
  }
} 

答案 3 :(得分:0)

这都是关于展示意图的。如果类的用户知道对类型参数施加了什么约束,他们可以设计更高保真度的代码。

另外,您的代码变得更加自我记录。

答案 4 :(得分:0)

第一个例子更灵活。只要T is Car

,您就可以将它与任何类型的对象列表一起使用

第二个例子说它必须是一辆汽车。您无法使用CompactCarTruck来使用秒。这是CompactCarTruck来自Car

答案 5 :(得分:0)

最上面一个创建一个类型的Car集合或任何来自Car像SportsCar的东西。

public class SportsCar : Car
{
 bool Turbo { get; set;}
}

在顶部,我可以创建一个跑车集合

var sports = new CarCollection<SportsCar>();
sports.Add(new SportsCar());
...
sports.First().Turbo = true;

在底部,我有

var cars = new CarCollection<Car>();
cars.Add(new SportsCar());
((SportsCar)cars.First()).Turbo = true;

它类似于拥有一组对象与一组字符串。 :)

答案 6 :(得分:0)

经过所有帖子后,我认为最有价值的用途之一尚未被讨论过(除非我错过了)。

您可以指定T必须实现多个接口。假设您希望它为ISomethingISomethingElseIOneOtherThing。您可以指定这三个特定接口。

static void DoSomething<T>(T myobj)
    where T : ISomething, ISomethingElse, IOneOtherThing
{
    myobj.Something();
    myobj.SomethingElse();
    myobj.OneOtherThing();
}

一个案例可能是您需要能够比较(IComparable)两个泛型类型并返回一个克隆(IClonable)的情况。或类似的东西。

我试图想出一种干净且可重复使用的方法,在没有此功能的情况下完成同样的事情。