泛型中的约束,其实就是针对类型参数的约束,限制类型参数的选择只能在某个特定范围内。其中的体现包括:限制类型参数必须是一个结构、限制类型参数必须是某个具体类型、限制类型参数必须派生自某个基类等等。在默认情况下,定义的泛型没有任何约束,这意味着在调用泛型时,可以使用任何数据类型作为类型参数。如果定义了约束,则在应用端调用泛型时,不传入符合约束条件的类型参数,编译器将提示错误。通过这种约束实现了编译前类型检查,确保了泛型在运行时对类型参数使用的安全性。
以上说的这种限制性的作用,只能体现约束表面的用意,这种用意是比较浅显易懂。但实际上泛型的约束还有另一层的用意:“定义约束可以告知编译器,类型参数具备了哪些能力”。我们在为某个泛型类或泛型方法编码时,面向的类型参数T,其实类似是一个模糊神秘的事物,因为你根部不会知道它有什么能力(属性、方法等成员),如果你想在编写泛型时使用类型参数T的某些能力,那么你就可以通过定义约束来实现。例如,你想要类型参数T调用“比较大小”的方法从而帮助你实现排序算法,你就可以定义一个泛型的约束:“要求类型参数必须实现IComparer接口”。这样一来,你的类型参数T,就能够在你编写泛型类的代码中“.”出Compare(比较的方法)。
基于上面对类型参数定义约束的用意分析,我针对约束主要的作用总结出以下两点:
以上通过文字描述的形式介绍了泛型中类型参数的约束,为了更加形象的体会其中的含义和作用,下面我将通过代码示例的形式介绍类型参数定义约束的使用方式。
假设我们在一个开发游戏的背景下,游戏比较简单,其中目前有两个职业:剑士和狙击手,并且后期随着游戏的普及会增加更多的职业。由于是战斗类型的游戏,所有每个职业都会使用特定的武器进行攻击,从而实现战斗的体验。对于该游戏职业设计相关的类图如下:

由于这只是一个为了讲解泛型约束的一个示例,所以并没有采用复杂的设计。由于剑士和狙击手两个职业都有相同的攻击行为,故而将攻击定义为了一个接口,具体的攻击内容将交由这两个职业类去实现。根据以上的类图的设计,相应的代码如下:
1 //攻击接口
2 interface IAttack
3 {
4 void MeleeAttacks(); //近战攻击
5 }
6
7 //剑士
8 class Swordman: IAttack
9 {
10 public Swordman() => Sword = "倚天剑";
11
12 public string Sword { get; set; }
13 public void MeleeAttacks()
14 {
15 Console.WriteLine("使用{0}进行刺击。", Sword);
16 }
17 }
18
19
20 //狙击手
21 class Sniper : IAttack
22 {
23 public Sniper() => Gun = "98k狙击步枪";
24 public string Gun { get; set; } //枪
25
26 public void MeleeAttacks()
27 {
28 Console.WriteLine("使用{0}进行射击。", Gun);
29 }
30 }
假设我们的游戏示例是一款战斗类型的游戏,那么其中所有的职业都需要进行战斗。对于这个共同的行为,正好可以借鉴泛型的使用思想:即不同类型存在相同处理逻辑,那么可以使用泛型作为一个代码模板,从而实现不同类型的通用化处理。我们计划将战斗的行为定义成一个泛型类,由这个泛型类统一实现各个职业的战斗。然而在编写战斗泛型类的时候,由于战斗必须要使用职业的攻击方法,但是我们在内部调用类型参数T并不能获取到相应的方法,编译器视乎将类型参数T看成了一个object类型。

怎么办?究竟如何能够在战斗泛型类中调用游戏角色的攻击方法呢?这个时候就轮到本文的主题“泛型的约束”闪亮登场了,接下来我们将针对战斗泛型类定义一个约束,在泛型类中使用类型参数T调用出攻击的方法:
1 /// <summary>
2 /// 各个职业的战斗
3 /// </summary>
4 class Combat<T> where T :IAttack
5 {
6 public Combat(T combatant)
7 {
8 _combatant = combatant;
9 }
10 private T _combatant;//参战者
11
12 public void Action()
13 {
14 Console.WriteLine("战斗开始");
15 _combatant.MeleeAttacks();
16 Console.WriteLine("战斗结束");
17 }
18
19 }
果不其然,成功的在战斗泛型类中调用了角色的攻击方法,这是因为设置了约束,类型参数T就可以根据约束的类型获取相应的能力。这一点也正好可以印证了本文开头总结泛型约束的作用之一:“对内部使用提供了更多能力,从而丰富功能的实现”。示例的代码已经基本编写完成,接下来我们就可以在应用端,使用战斗泛型类针对不同的角色实施战斗行为了。

假设你的小伙伴正在另一头在编写游戏中关于NPC部分的代码,他得知你编写了可以实现各种职业进行战斗的泛型类,于是乎他悄悄的使用一个NPC的对象来使用你的战斗泛型类。但是NPC在实际的需求中并没有实现攻击接口。NPC类的代码结构如下:

我们在假定,泛型的约束不能够对外部传入的类型参数(NPC类)起到限制作用。那么这个NPC的“战斗”情况可想而知,NPC是没有主动攻击的方法的,他盲目的使用战斗泛型类,只会无情的面临“死亡”。还好,我们定义的类型参数约束对此进行了把关,我们约束的规则是:要求类型参数必须实现攻击接口。而NPC并没有实现攻击接口,所以对于NPC使用战斗泛型类时编译器会提示错误。

通过NPC滥用泛型类的这个示例,就可以从分的体现出本文开头总结泛型约束的作用之一:“对外部使用形成了限制条件,从而确保泛型的类型安全”。
理解泛型的约束,可能会觉得它是很语义化、片面化的东西。殊不知,其实泛型约束在实际中最有作用的是,为类型参数提供能力,让我们在编码的过程中更有针对性。所以学习不能只求表面,必须通过反复思考,才能让获取的知识更加立体。
对于泛型约束的使用方式,除了本文示例中要求实现一个特定接口的方式,另外还有很多使用方式。我们不可能将每一个使用细节了然于心,但是必须搞清楚事物的本质,以致于知道为什么有它的存在、在什么样的情况下使用它。当不同的应用场景发生时,我们在结合当下应用场景的实际情况,通过查阅文档来制定具体的方针。
文章目录前言约束硬约束的轨迹优化Corridor-BasedTrajectoryOptimizationBezierCurveOptimizationOtherOptions软约束的轨迹优化Distance-BasedTrajectoryOptimization优化方法前言可以看看我的这几篇Blog1,Blog2,Blog3。上次基于MinimumSnap的轨迹生成,有许多优点,比如:轨迹让机器人可以在某个时间点抵达某个航点。任何一个时刻,都能数学上求出期望的机器人的位置、速度、加速度、导数。MinimumSnap可以把问题转换为凸优化问题。缺点:MnimumSnap可以控制轨迹一定经过中间的
在我的routes.rb文件中,我想使用rails3中的子域约束功能,但是我想从catchall路由中排除某些域。我不想在特定的子域中有特定的Controller。这样做的最佳做法是什么。#thissubdomainidontwantallofthecatchallroutesconstraints:subdomain=>"signup"doresources:usersend#hereIwanttocatchallbutexcludethe"signup"subdomainconstraints:subdomain=>/.+/doresources:carsresources:sta
我有一个表students,字段为ward_id,我必须创建一个名为guardian_users的表,字段为id,ward_id,email,guardian_id,hashed_password等现在我必须添加约束外键。学生中的任何更新/删除/编辑/插入应该对guardian_users具有相同的效果。我如何在Rails2.3.5中做到这一点?students表存在,但其他表还不存在。 最佳答案 您要么需要foreign_key_migrations插件或#execute方法。假设您使用插件:classCreateGuardi
这个问题是对这里提出的问题的扩展:Usingfactory_girlinRailswithassociationsthathaveuniqueconstraints.Gettingduplicateerrors所提供的答案对我来说非常有效。这是它的样子:#Createsaclassvariableforfactoriesthatshouldbeonlycreatedonce.moduleFactoryGirlclassSingleton@@singletons={}defself.execute(factory_key)begin@@singletons[factory_key]=Fa
我在使用子域约束进行rspec路由测试时遇到问题。特别是我有一条路线constraints:subdomain=>"api"doresources:sign_ups,:only=>[:create]end和(除其他外)测试it"doesallowcreationofsignups"do{:post=>"/sign_ups"}.shouldroute_to(:controller=>"sign_ups",:action=>"create",)end如果我删除子域约束,则此测试通过,但它会失败。我必须告诉rspec使用子域,但我不知道如何使用TIA安迪 最佳答案
有什么方法可以让url_for在Action调度路由期间根据request.host返回url吗?mountCollaborate::Engine=>'/apps/collaborate',:constraints=>{:host=>'example.com'}mountCollaborate::Engine=>'/apps/worktogether'示例:当用户在example.com主机上时collaborate_path=>/apps/collaborate当用户在任何其他主机上时collaborate_path=>/apps/worktogether经过大量研究,我意识到Rou
如何在不重新打开文件的情况下将文件的“指针”重置为开头?(类似于C中的fseek?)例如,我有一个文件,我想为两种模式进行grep:f=open('test')=>#f.grep(/llo/)=>["Helloworld\n"]f.grep(/wo/)=>[]是否可以在不重新打开文件的情况下重置f?注意:我不是在寻找解决方法;我可以自己想一些;)。 最佳答案 使用rewind将ios定位到输入的开头,将lineno重置为零。f=File.new("testfile")f.readline#=>"Thisislineone\n"f.r
我正在使用一个Rails2.2项目来更新它。我正在用工厂(使用factory_girl)替换现有的固定装置,但遇到了一些问题。问题在于表示具有查找数据的表的模型。当我使用两个具有相同产品类型的产品创建购物车时,每个创建的产品都会重新创建相同的产品类型。此错误来自对ProductType模型的唯一验证。问题演示这是我创建购物车并将其分成几部分的单元测试的结果。我必须这样做才能解决这个问题。不过,这仍然说明了问题。我会解释。cart=Factory(:cart)cart.cart_items=[Factory(:cart_item,:cart=>cart,:product=>Factory
在下面的链接(MDN站点)上它说“字符串泛型是非标准的,已弃用并且将来可能会被删除。请注意,如果不使用下面提供的填充程序,您不能跨浏览器依赖它们。“他们所指的方法是否是他们在该声明下方提供的垫片中列出的方法?这是我见过的唯一提到短语“字符串泛型”的地方,所以让我很困惑。对于数组泛型也有同样的问题,因为该站点也提到了类似的情况。https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String#String_generic_methodshttps://developer.mozi
我需要根据变量在Typescript泛型类中的类型设置默认值,如下所示classMyClass{myvariable:T//HereIwanttosetthevalueofthisvariable//withthedefaultvalueofthetypepassedin'T'}例如,如果T是数字,那么变量myvariable的默认值应该是“0”,同样对于字符串,它应该是空字符串等等。 最佳答案 您不能这样做,因为T的实际类型只会在运行时才知道。你可以做什么:abstractclassMyClass{myvariable:T;con