C#里叫抽象类,而不是纯虚类。
最近接触c#,对其中的接口interface,非常不能理解。
接口存在的意义是什么?
它比纯虚类也即抽象类,有何优点?C#为何不用多继承而采用接口的方式?
我们知道C++的多继续肯定比C#的接口更灵活强大。纯虚类里也可以有实现的代码,而使用接口的话,则不能有实现代码。
假设一组类继承自同一个接口,其中大部分行为是不同的,而其中又有一部分的行为是相同的,按照接口的定义,这些实现接口的类都必须写自己的实现。这个问题如何解决?
假设一个接口有一组接口,这里总觉着十分别扭,就说一个接口有一组公有成员。
实现这个接口的意义似乎仅在于多态。对于代码没有什么帮助。因为接口没有任何代码实现。
实现这个接口对于实际的代码编写似乎没有任何帮助。
求助。
接口是一种特殊的类,从某种意义上来说,可以把接口当作纯虚类来看。
但跟纯虚类有着本质的区别:
1——
接口里的方法、属性、事件没有访问控制,它们的访问控制由接口的访问属性来设定,而纯虚类则可以单独控制抽象函数、属性事件的访问行为
2——
接口里只能定义方法、事件或属性,而纯虚类中还可以定义普通的方法、属性、事件、变量等(当然,这样也就不叫纯虚类了)
3——
.NET的类继承机制里,一个类只能有一个基类,不能有多个,但可以实现(继承)多个不同的接口。
"假设一组类继承自同一个接口,其中大部分行为是不同的,而其中又有一部分的行为是相同的,按照接口的定义,这些实现接口的类都必须写自己的实现。这个问题如何解决?"
这个问题是没办法解决的,这个是.NET的继承机制限制了的。.NET的这个继承机制对于写C++的程序员来说很恼火,但C++语言的强大是付出了不严谨的代价的。.NET里相对严谨很多,使得现在.NET程序员满天飞,但C++程序员却不可以。C#是更高一级的程序设计语言,相比于C++就象C++相比于ASM一样。
对于这个问题的解决,见仁见智,一般都是通过HELPER的形式实现,或者写重复的代码。很痛苦,我也经常为这个问题头痛。
“假设一个接口有一组接口,这里总觉着十分别扭,就说一个接口有一组公有成员。
实现这个接口的意义似乎仅在于多态。对于代码没有什么帮助。因为接口没有任何代码实现。
实现这个接口对于实际的代码编写似乎没有任何帮助。”
对于这个问题,那是你没有对接口真正的了解。
接口其实就是定义了一套规范,让我们可以基于这一套规范进行数据传递、行为处理。
比如,我曾经写了一个显示下载进度条的控制代码,开始是通过写一个UserControl来实现,后来,这个进度条又可能需要放置到MDI窗口的状态条(以前这里是不能嵌入UserControl的),于是,对于进度条进度的控制我就必须定义两个方法,分别传递自定义的进度条控制控件和状态条标签控件,很麻烦。
在后来的代码整合中,把进度条的控制(调用)写到了业务逻辑层,甚至是公共服务模块,此时,前端的UI对象是没办法传递给服务模块的(不想因为这个而在业务层或公共服务模块去定义这样的UI),更没必要让业务层或公共服务模块去引用Forms名称空间(这个代码也许还要被别的地方使用,如在WPF、Silverlight、ASP.NET中),此时,定义一套接口规范进度条的控制就显得必要了。
接口其实就是抽象类,你ildasm查看一下中间语言,接口其实就是公共抽象类;
接口还可以继承接口,接口用于抽象工厂,简单工厂什么的,觉得特别好用!
写接口不是为了实现什么功能 ,重要的是使用接口的思想,看下设计模式的书籍,就知道接口好处
例子1:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace IGameLi 7 { 8 class Program 9 { 10 static void Main(string[] args) 11 { 12 //生成怪物 13 Monster monster1 = new Monster("小怪A", 50); 14 Monster monster2 = new Monster("小怪B", 50); 15 Monster monster3 = new Monster("关主", 200); 16 Monster monster4 = new Monster("终极Boss", 1000); 17 18 //生成角色 19 Role role = new Role(); 20 21 //木剑攻击 22 role.WeaponTag = "WoodSword"; 23 role.Attack(monster1); 24 25 //铁剑攻击 26 role.WeaponTag = "IronSword"; 27 role.Attack(monster2); 28 role.Attack(monster3); 29 30 //魔剑攻击 31 role.WeaponTag = "MagicSword"; 32 role.Attack(monster3); 33 role.Attack(monster4); 34 role.Attack(monster4); 35 role.Attack(monster4); 36 role.Attack(monster4); 37 role.Attack(monster4); 38 39 Console.ReadLine(); 40 } 41 } 42 43 /// <summary> 44 /// 怪物 45 /// </summary> 46 internal sealed class Monster 47 { 48 /// <summary> 49 /// 怪物的名字 50 /// </summary> 51 public string Name { get; set; } 52 53 /// <summary> 54 /// 怪物的生命值 55 /// </summary> 56 public int HP { get; set; } 57 58 public Monster(string name, int hp) 59 { 60 this.Name = name; 61 this.HP = hp; 62 } 63 } 64 65 /// <summary> 66 /// 角色 67 /// </summary> 68 internal sealed class Role 69 { 70 private Random _random = new Random(); 71 72 /// <summary> 73 /// 表示角色目前所持武器的字符 74 /// </summary> 75 public string WeaponTag { get; set; } 76 77 /// <summary> 78 /// 攻击怪物 79 /// </summary> 80 /// <param name="monstr"></param> 81 public void Attack(Monster monstr) 82 { 83 if (monstr.HP <= 0) 84 { 85 Console.WriteLine("怪物已死"); 86 return; 87 } 88 if ("WoodSword" == this.WeaponTag) 89 { 90 monstr.HP -= 20; 91 if (monstr.HP <= 0) 92 { 93 Console.WriteLine("攻击成功!怪物" + monstr.Name + "已死亡"); 94 } 95 else 96 { 97 Console.WriteLine("攻击成功!怪物" + monstr.Name + "损失20HP"); 98 } 99 } 100 else if ("IronSword" == this.WeaponTag) 101 { 102 monstr.HP -= 50; 103 if (monstr.HP <= 0) 104 { 105 Console.WriteLine("攻击成功!怪物" + monstr.Name + "已死亡"); 106 } 107 else 108 { 109 Console.WriteLine("攻击成功!怪物" + monstr.Name + "损失50HP"); 110 } 111 } 112 else if ("MagicSword" == this.WeaponTag) 113 { 114 int loss = (_random.NextDouble() < 0.5) ? 100 : 200; 115 monstr.HP -= loss; 116 if (200 == loss) 117 { 118 Console.WriteLine("出现暴击!!!"); 119 } 120 if (monstr.HP <= 0) 121 { 122 Console.WriteLine("攻击成功!怪物" + monstr.Name + "已死亡"); 123 } 124 else 125 { 126 Console.WriteLine("攻击成功!怪物" + monstr.Name + "损失" + loss.ToString() + "HP"); 127 } 128 } 129 else 130 { 131 Console.WriteLine("角色手里没有武器,无法攻击!"); 132 } 133 } 134 } 135 }
例子2:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace IGameYu 7 { 8 class Program 9 { 10 static void Main(string[] args) 11 { 12 Role role = new Role(); 13 Monster monster1 = new Monster("小怪A",50); 14 Monster monster2 = new Monster("小怪B", 50); 15 Monster monster3 = new Monster("关主", 200); 16 Monster monster4 = new Monster("终极Boss", 1000); 17 18 role.Weapon = new WoodSword(); 19 role.Attack(monster1); 20 21 role.Weapon = new IronSword(); 22 role.Attack(monster2); 23 role.Attack(monster3); 24 25 role.Weapon = new MagicSword(); 26 role.Attack(monster3); 27 role.Attack(monster4); 28 role.Attack(monster4); 29 role.Attack(monster4); 30 role.Attack(monster4); 31 role.Attack(monster4); 32 33 Console.ReadLine(); 34 35 } 36 } 37 38 internal interface IAttackStrategy 39 { 40 void AttackTarget(Monster monster); 41 } 42 43 internal sealed class WoodSword : IAttackStrategy 44 { 45 public void AttackTarget(Monster monster) 46 { 47 monster.Notify(20); 48 } 49 } 50 51 internal sealed class IronSword : IAttackStrategy 52 { 53 public void AttackTarget(Monster monster) 54 { 55 monster.Notify(50); 56 } 57 } 58 59 internal sealed class MagicSword : IAttackStrategy 60 { 61 private Random _random = new Random(); 62 public void AttackTarget(Monster monster) 63 { 64 int loss = (_random.NextDouble() < 0.5) ? 100 : 200; 65 if (200 == loss) 66 { 67 Console.WriteLine("出现暴击!!!"); 68 } 69 monster.Notify(loss); 70 } 71 } 72 73 internal sealed class Role 74 { 75 /// <summary> 76 /// 角色目前所持武器 77 /// </summary> 78 public IAttackStrategy Weapon { get; set; } 79 80 /// <summary> 81 /// 攻击怪物 82 /// </summary> 83 /// <param name="monster"></param> 84 public void Attack(Monster monster) 85 { 86 this.Weapon.AttackTarget(monster); 87 } 88 } 89 90 /// <summary> 91 /// 怪物 92 /// </summary> 93 internal sealed class Monster 94 { 95 /// <summary> 96 /// 怪物的名字 97 /// </summary> 98 private string Name { get; set; } 99 100 /// <summary> 101 /// 怪物的生命 102 /// </summary> 103 private int HP { get; set; } 104 105 public Monster(string name, int hp) 106 { 107 this.Name = name; 108 this.HP = hp; 109 } 110 111 /// <summary> 112 /// 怪物被攻击时,调用的方法,用来处理被攻击的状态更改 113 /// </summary> 114 /// <param name="loss"></param> 115 public void Notify(int loss) 116 { 117 if (this.HP <= 0) 118 { 119 Console.WriteLine("此怪物已死!"); 120 return; 121 } 122 this.HP -= loss; 123 if (this.HP <= 0) 124 { 125 Console.WriteLine("怪物" + this.Name + "被打死!"); 126 } 127 else 128 { 129 Console.WriteLine("怪物" + this.Name + "损失" + loss.ToString() + "HP"); 130 } 131 } 132 } 133 }
例子2和例子1很大的区别,就是OCP原则,接口好处还是很多的,没达到一定的功底,不好表述
使用has而不是is关系的时候,使用接口确实有诸多方便,不过这里使用继承似乎也一样的。
而在isa关系的时候,接口不能定义默认行为的特点就明显的暴露出来了。
@primer code: 我不是很会描述,这篇博文写了不错,关于接口和抽象类的区别 ,看了一定会有收获的 http://www.cnblogs.com/huihui-gohay/archive/2009/12/13/1623070.html
C#中使用interface来现实C++中的多继承,可以避免,C++多继承的那种复杂性,使得使用上更简便。像你所说的问题,完全可以用一个类实现这些接口之后,再由其他类来派生解决,不是一定要用多继承来实现的。
那这样的话,接口存在的意义是什么?直接由一个基类实现这些行为不就好了。
设计理念上的接口和抽象类可以做一个简单、形象而又有趣的比喻
铁门、木门都是门(抽象类),你想要个门我给不了(不能实例化),但我可以给出具体的铁门或木门(多态);
而且只能是门,你不能说它是窗(单继承);一个门可以有报警功能(接口)。门(抽象类)定义了什么,
接口(报警功能)规定了你能做什么
抽象类体现了一种继承关系,若想要使继承关系合理化,父类和派生类之间必须存在 is -- a的关系,
即父类和派生类在概念、本质上应该是相同的。接口则不然,它并不要求接口的实现者和接口定义在概念、本质上一致,仅实现接口定义的规范和标准即可。
接口是一种规范和标准,用于约束类的行为,接口可以将方法的特征和实现分割开来,
这种分割体现在接口常常代表一个角色,它包装与该角色相关的操作和属性,而实现
这个接口的泪便是扮演角色的演员,一个角色可以由不同的演员饰演,而这些演员
之间除了扮演一个共同的角色之外,并不要求其他共同点
抽象类和子类往往形成一个小体系,如Stream类形成的流体系、DbDataReader类形成的数据库读取体系。
通常,不同体系之间有时会有相同的行为,比如上面两个体系都占用外部资源,使用完后需要释放资源,因此两者都实现了IDisposable接口,用于释放资源。
那么,他们共同的基类是Object,为何不在Object中加入IDisposable接口的方法?
因为其他类的基类也是Object,他们很多并没有占用资源的功能,因此不合适。
为何不再构造出一个类,做为这两者的基类,用于实现IDisposable接口的方法?
假设,现有10个体系,又出现了一个体系跟其中5个有相同行为。如果使用抽象类,就意味着,添加一个体系,我要修改前5个体系。那么如果再出现一个体系,跟其中3个相同,跟另外4个相同,那怎么办?如果再出现,再出现…………
对于这种跨体系的功能,使用接口就很灵活,有这个功能就继承,没有就不继承,不影响以前的类。
除此之外,基类只有一个,而接口可以有多个。
基类可以有代码实现,而接口本身不能有实现代码。
就像篮球运动中上篮和跳投形式不同但都能得分,要根据情况灵活运用,只上篮或只跳投都不算合格。
接口实现的是has-a的功能, 表示继承接口的对象实现了接口定义的方法。
基类提供的是is-a的功能,表示子类是基类的一种派生。
对于你的假设:
假设一:对于相同的部分,可以抽象出基类,由子类继承,然后再由子类去继承自己特殊功能的接口。
比如:Animal可以作为一个基类,定义动物的公共属性和公共方法。
Bird继承自Animal,通过多态实现动物的公共属性和方法,但是对于鸟的特殊功能,比如说Fly(),则可以通过IFlyable接口来进行继承。同样的,对于IFlyable接口来说,不只是鸟才能继承这个接口。
假设二:接口的意义在于隔离和单一职责。如果一个接口定义了毫不相干的几个功能或者属性,那么这个接口是有坏味道的。
考虑以下场景:
我们都知道现实的工厂需要工人们进行工作而不是吃白饭。所以在资本家眼中,他们只关心工人们Work()这个功能,而不会在意工人们Eat(),Sleep()等功能。甚至,归根结底,资本家要的只是一个能够提供Work()功能的对象,而不需要关这个功能是工人提供的还是机器提供的。当有需要的时候,资本家会为了提高生产效率而把效率低的Work()功能实现替换成效率高的Work()。这个时候,我们采用的是基类实现,则当我们在修改Work()功能的实现的时候(将工人替换成机器),则必须对基类和子类都进行修改。而如果我们采用的是接口实现,则只需要将接口实现的对象替换成高效率的实现就行了。
ps:建议去看下 设计模式这本书的C#版本,了解下c#相关的设计原则。
接口就是一个开放给大家接近你引用你的通道口,假设有个动物类class animal,而猴子类class monkey;小狗类class Dogs;继承了动物了,那么当你要new一个小狗类只需要这样
Dogs 腊肠狗=new animal();就行了,不用去找对应的狗类型,省去了很多是,接口既有多继承的特性,又有指针调用方法的用途。
不知道回答到不到点,希望你要见怪。
接口就是用于 聚合公调用。