首页 新闻 会员 周边 捐助

C# 中的接口求解

0
悬赏园豆:100 [已解决问题] 解决于 2012-05-05 23:07

C#里叫抽象类,而不是纯虚类。

 

最近接触c#,对其中的接口interface,非常不能理解。

接口存在的意义是什么?

它比纯虚类也即抽象类,有何优点?C#为何不用多继承而采用接口的方式?

 

我们知道C++的多继续肯定比C#的接口更灵活强大。纯虚类里也可以有实现的代码,而使用接口的话,则不能有实现代码。

假设一组类继承自同一个接口,其中大部分行为是不同的,而其中又有一部分的行为是相同的,按照接口的定义,这些实现接口的类都必须写自己的实现。这个问题如何解决?

 

假设一个接口有一组接口,这里总觉着十分别扭,就说一个接口有一组公有成员。

实现这个接口的意义似乎仅在于多态。对于代码没有什么帮助。因为接口没有任何代码实现。

实现这个接口对于实际的代码编写似乎没有任何帮助。

 

求助。

杨海龙的主页 杨海龙 | 初学一级 | 园豆:26
提问于:2012-05-02 15:52
< >
分享
最佳答案
0

接口是一种特殊的类,从某种意义上来说,可以把接口当作纯虚类来看。

但跟纯虚类有着本质的区别:

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中),此时,定义一套接口规范进度条的控制就显得必要了。

收获园豆:40
无之无 | 大侠五级 |园豆:5095 | 2012-05-02 16:20
其他回答(7)
0

接口其实就是抽象类,你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原则,接口好处还是很多的,没达到一定的功底,不好表述

收获园豆:30
xu_happy_you | 园豆:222 (菜鸟二级) | 2012-05-02 16:07

使用has而不是is关系的时候,使用接口确实有诸多方便,不过这里使用继承似乎也一样的。

而在isa关系的时候,接口不能定义默认行为的特点就明显的暴露出来了。

支持(0) 反对(0) 杨海龙 | 园豆:26 (初学一级) | 2012-05-02 16:31

@primer code: 我不是很会描述,这篇博文写了不错,关于接口和抽象类的区别 ,看了一定会有收获的    http://www.cnblogs.com/huihui-gohay/archive/2009/12/13/1623070.html

支持(0) 反对(0) xu_happy_you | 园豆:222 (菜鸟二级) | 2012-05-02 16:44
0

C#中使用interface来现实C++中的多继承,可以避免,C++多继承的那种复杂性,使得使用上更简便。像你所说的问题,完全可以用一个类实现这些接口之后,再由其他类来派生解决,不是一定要用多继承来实现的。

sinhbv | 园豆:2579 (老鸟四级) | 2012-05-02 16:13

那这样的话,接口存在的意义是什么?直接由一个基类实现这些行为不就好了。

支持(0) 反对(0) 杨海龙 | 园豆:26 (初学一级) | 2012-05-02 16:31
0

设计理念上的接口和抽象类可以做一个简单、形象而又有趣的比喻

 

铁门、木门都是门(抽象类),你想要个门我给不了(不能实例化),但我可以给出具体的铁门或木门(多态);

而且只能是门,你不能说它是窗(单继承);一个门可以有报警功能(接口)。门(抽象类)定义了什么,

接口(报警功能)规定了你能做什么

 

抽象类体现了一种继承关系,若想要使继承关系合理化,父类和派生类之间必须存在 is -- a的关系,

即父类和派生类在概念、本质上应该是相同的。接口则不然,它并不要求接口的实现者和接口定义在概念、本质上一致,仅实现接口定义的规范和标准即可。

 

接口是一种规范和标准,用于约束类的行为,接口可以将方法的特征和实现分割开来,

这种分割体现在接口常常代表一个角色,它包装与该角色相关的操作和属性,而实现

这个接口的泪便是扮演角色的演员,一个角色可以由不同的演员饰演,而这些演员

之间除了扮演一个共同的角色之外,并不要求其他共同点

┢┦偉 | 园豆:1240 (小虾三级) | 2012-05-02 18:35
0

抽象类和子类往往形成一个小体系,如Stream类形成的流体系、DbDataReader类形成的数据库读取体系。

通常,不同体系之间有时会有相同的行为,比如上面两个体系都占用外部资源,使用完后需要释放资源,因此两者都实现了IDisposable接口,用于释放资源。

那么,他们共同的基类是Object,为何不在Object中加入IDisposable接口的方法?

因为其他类的基类也是Object,他们很多并没有占用资源的功能,因此不合适。

为何不再构造出一个类,做为这两者的基类,用于实现IDisposable接口的方法

假设,现有10个体系,又出现了一个体系跟其中5个有相同行为。如果使用抽象类,就意味着,添加一个体系,我要修改前5个体系。那么如果再出现一个体系,跟其中3个相同,跟另外4个相同,那怎么办?如果再出现,再出现…………

对于这种跨体系的功能,使用接口就很灵活,有这个功能就继承,没有就不继承,不影响以前的类。

除此之外,基类只有一个,而接口可以有多个。

基类可以有代码实现,而接口本身不能有实现代码。

就像篮球运动中上篮和跳投形式不同但都能得分,要根据情况灵活运用,只上篮或只跳投都不算合格。

收获园豆:10
许两会 | 园豆:219 (菜鸟二级) | 2012-05-02 20:15
0

接口实现的是has-a的功能, 表示继承接口的对象实现了接口定义的方法。

基类提供的是is-a的功能,表示子类是基类的一种派生。

对于你的假设:

假设一:对于相同的部分,可以抽象出基类,由子类继承,然后再由子类去继承自己特殊功能的接口。

比如:Animal可以作为一个基类,定义动物的公共属性和公共方法。

Bird继承自Animal,通过多态实现动物的公共属性和方法,但是对于鸟的特殊功能,比如说Fly(),则可以通过IFlyable接口来进行继承。同样的,对于IFlyable接口来说,不只是鸟才能继承这个接口。

 假设二:接口的意义在于隔离和单一职责。如果一个接口定义了毫不相干的几个功能或者属性,那么这个接口是有坏味道的。

考虑以下场景:

我们都知道现实的工厂需要工人们进行工作而不是吃白饭。所以在资本家眼中,他们只关心工人们Work()这个功能,而不会在意工人们Eat(),Sleep()等功能。甚至,归根结底,资本家要的只是一个能够提供Work()功能的对象,而不需要关这个功能是工人提供的还是机器提供的。当有需要的时候,资本家会为了提高生产效率而把效率低的Work()功能实现替换成效率高的Work()。这个时候,我们采用的是基类实现,则当我们在修改Work()功能的实现的时候(将工人替换成机器),则必须对基类和子类都进行修改。而如果我们采用的是接口实现,则只需要将接口实现的对象替换成高效率的实现就行了。

ps:建议去看下 设计模式这本书的C#版本,了解下c#相关的设计原则。

收获园豆:10
菜鸟老了 | 园豆:145 (初学一级) | 2012-05-03 23:54
0

接口就是一个开放给大家接近你引用你的通道口,假设有个动物类class animal,而猴子类class monkey;小狗类class Dogs;继承了动物了,那么当你要new一个小狗类只需要这样

Dogs  腊肠狗=new  animal();就行了,不用去找对应的狗类型,省去了很多是,接口既有多继承的特性,又有指针调用方法的用途。

     不知道回答到不到点,希望你要见怪。

黑夜猫头鹰 | 园豆:3 (初学一级) | 2012-05-04 16:35
0

接口就是用于 聚合公调用。

Agiler | 园豆:202 (菜鸟二级) | 2013-09-28 10:49
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册