简体   繁体   English

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

[英]What is the advantage of using constraints on C# Generics

Can someone tell me what the difference is between the following 有人能告诉我以下几点之间的区别

public class CarCollection<T>:List<T> where T:Car{}

and

public class CarCollection:List<Car>{}

To me they seem to do the same thing, create type-safe collection of "Car" objects. 对我来说,他们似乎做同样的事情,创建类型安全的“汽车”对象集合。

Constraints are nice when you need some information about the generic type argument. 当您需要有关泛型类型参数的一些信息时,约束很好。 By constraining the type you are not only limiting the set of valid type arguments but you are also giving yourself the ability to make compile-time decisions based on the common elements of that set of types. 通过约束类型,您不仅限制了有效类型参数的集合,而且还让您自己能够根据该类型的公共元素做出编译时决策。

In your specific example the generic constraint does not provide any benefit but in other circumstances they can. 在您的具体示例中,通用约束不提供任何好处,但在其他情况下它们可以。 Consider this example: 考虑这个例子:

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

This method cannot compile because the compiler knows (as well I should have when I wrote the method) that the generic type argument could possibly be a value type which cannot be set to null . 此方法无法编译,因为编译器知道(当我编写方法时我应该知道)泛型类型参数可能是一个不能设置为null的值类型。 By adding a constraint I can make this code compile: 通过添加约束,我可以编译此代码:

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

I have now limited the valid set of generic type arguments to just classes. 我现在已经将有效的泛型类型参数集限制为只有类。 Since the compiler now knows that T must be a class it allows me to return null since there are no scenarios in which null could not be returned. 由于编译器现在知道T必须是类它可以让我回到null ,因为没有任何场景中, null不能退还。 While this is not a terribly useful example I am sure you can extrapolate ways that this can be helpful. 虽然这不是一个非常有用的例子,但我相信你可以推断这有用的方法。

If you implement a generic that will perform comparisons, the where clause is necessary. 如果实现将执行比较的泛型,则where子句是必需的。 For example: 例如:

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!");
    }
}

This is a specific example, but it illustrates the concept that your where clause identifies that your generic type will adhere to a type, and thus is able to utilize functionality. 这是一个具体示例,但它说明了where子句标识您的泛型类型将遵循类型,从而能够利用功能的概念。

I think that the second format is misleading: the "Car" is a template parameter, not a reference to the "Car" class which you presumably defined elsewere. 我认为第二种格式是误导性的:“Car”是一个模板参数,而不是对你可能定义的“Car”类的引用。

The first format lets you invoke members (methods and properties) of the Car class on instances of T within the template class. 第一种格式允许您在模板类中的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();
      }
  }
} 

It's all about showing intent. 这都是关于展示意图的。 If the user of your class knows what constraints are put on a type parameter, they can design their own code with higher fidelity. 如果您的类的用户知道对类型参数施加了什么约束,他们可以设计自己的代码具有更高的保真度。

Plus your code becomes more self documenting. 此外,您的代码变得更加自我记录。

The first example is much more flexible. 第一个例子更灵活。 You can use it with a list of any kind of object as long as T is Car 只要T is Car就可以将它与任何类型的对象列表一起使用

The second example says it has to be a car. 第二个例子说它必须是一辆汽车。 You could not use the second with a CompactCar or Truck . 您不能将第二个用于CompactCarTruck That is if CompactCar or Truck derived from Car 那就是CompactCarTruck来自Car

The top one creates a typed collection of Car or anything that derives from Car like SportsCar. 最上面的一个创建了一个类型的Car集合或任何来自Car像SportsCar的东西。

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

On the top, I could create a collection of sportscars 在顶部,我可以创建一个跑车集合

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

On the bottom I'd have 在底部,我有

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

It's similar to having a collection of objects vs a collection of strings. 它类似于拥有一组对象与一组字符串。 :) :)

Having gone through all the posts, I think one of the most valuable uses has not been discussed (unless I missed it). 经过所有帖子后,我认为其中一个最有价值的用途尚未讨论过(除非我错过了)。

You can specify that T has to implement more than one interface. 您可以指定T必须实现多个接口。 Say you want it to be ISomething , ISomethingElse , and IOneOtherThing . 假设您希望它是ISomethingISomethingElseIOneOtherThing You can specify those three specific interfaces. 您可以指定这三个特定接口。

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

A case may be where you need to be able to compare (IComparable) two generic types and return a clone (IClonable) of one. 一个案例可能是您需要能够比较(IComparable)两个泛型类型并返回一个克隆(IClonable)的情况。 Or something like that. 或类似的东西。

I'm trying to think of a clean and reusable way to accomplish this same thing without this feature. 我试图想出一种干净且可重复使用的方法来完成同样的事情而没有这个功能。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM