zoukankan      html  css  js  c++  java
  • 转载:接口包含内容概述1——接口相关概述及接口中属性的实现

    转自:http://www.cnblogs.com/yangmingming/archive/2010/02/24/1672569.html

    临近年终,和一朋友去应聘,偶见一C#选择题,问道:接口中不能包含什么?答案有:方法,属性,事件,字段。(我比较确定,应该选择字段,然而对于其确切原因却不能详述。这两天看到Steven John Metsker 的《C# 设计模式》中讲到“接口型模式设计”时,详细讲述了接口,以及接口中内容 。在此借助其讲述,将其做一概要。

    接口和抽象类:

         为了更好的理解接口,最常见的问题莫过于接口和抽象类的区别。如前所述,博文《IL应用之——用IL分析接口的本质》 中已详细概述,用IL反编译工具,可以得出接口的本质,即编译为中间语言时,是采用抽象类的实现机制实现的(实现多态!)。然而接口和抽象类却存在实实在在的差异,具体体现在:

    1.一个类可以实现任意多个接口,但是最多只能对一个抽象类进行子类化;

    2.一个抽象类可以包括非抽象方法,而一个接口的所有方法在效果上都是抽象的;

    3.一个抽象类可以声明并使用变量,而一个接口不能;

    4.一个抽象类中的方法的访问修饰符可以是:public,internal,protected,protected internal,或者private,而接口中默认情况下都是public,而且在声明接口成员时,不允许使用访问修饰符;

    5.一个抽象类可以定义构造函数,而一个接口不能;

    上述5点区别主要是使用上,之所以将其区分开,是因为其使用场景是不同的 ,即功用性所在,主要可概括为:抽象类用在具有族层关系的继承关系上;而接口用在具有功能性意义上。

    接口中属性的实现:

         通常情况下接口中都是对应方法,然而其确实可以对属性 进行同样操作(以前只是知道,现在用代码实现之。)先看如下代码:

    //Description: 通过建立接口IFlyable和实现类Bird,演示接口中属性的实现方式

    //CopyRight: http://www.cnblogs.com/yangmingming

    //Notes: 为简便,将接口、类的建立,和验证代码放于一起

    namespace ContentOfInterfaceDemo
    {
        
    public interface IFlyable
        {
            
    int Distance { get; }
            
    string TypeOfFly { getset; }
        }
        
    public class Bird : IFlyable
        {
            
    private int distance;
            
    private string typeOfFly;
            
    public int Distance
            {
                
    get { return distance; }
            }
            
    public string TypeOfFly
            {
                
    get { return typeOfFly; }
                
    set { typeOfFly = value; }
            }
        }
        
    class Program
        {
            
    static void Main(string[] args)
            {
                Bird bird 
    = new Bird();
               
    // bird.Distance = 10;
                bird.TypeOfFly = "Using his Wings.";
                Console.WriteLine(
    "distance:{0},TypeOfFly:{1}", bird.Distance, bird.TypeOfFly);
            }
        }
    }

    经调试,结果如下:

     

     可见我们实现了接口属性的继承!

    接口中属性实现分析:

         之所以属性可以像方法一样实现继承效果,从本质上并不难理解。在博文中《初识Ildasm.exe——IL反编译的实用工具》中,用IL代码讲述了属性的实现机制,即转变为对应的get和set函数,这样 在此文中所谓的属性实现,都可以转化为方法的实现,即问题迎刃而解,呵呵~(由此可见IL代码的分析重要性!)

    附:问题

    一个接口中可以包含方法、C#属性、以及索引器。一个接口中也可以包含事件,但是不能包含委托,为什么?下节继续。。。

    委托概述:

       在 C 语言 的众多种类型指针中,有一种叫做“函数指针”,即是指函数的入口地址。在 C# 中委托的概念与其相类似。而与C 语言中函数指针不同的是,.NET 中的委托是类型安全的。

    委托的使用:

         同使用一个类相类似,首先定义要使用的委托,这里规定了委托所代表的哪种类型的方法; 其次创建该委托的一个或多个实例。这里举一示范代码:

    //Description: 通过建立委托实例,表现委托的定义及使用方法

    //CopyRight: http://www.cnblogs.com/yangmingming

    //Notes: 为简便,将委托的定义,和验证代码放于一起

    namespace DelegateDemo
    {
        
    class Program
        {
            
    //委托的定义,制定对应方法的类型
            public delegate int DelegateShowInt(int i);
            
    public static int ShowData(int i)
            {
                
    return i + 1;
            }


            
    static void Main(string[] args)
            {
                DelegateShowInt showInt 
    = new DelegateShowInt(ShowData);
                Console.WriteLine(
    "The Result is {0}", showInt(1));
            }
        }
    }

    最后调试结果为:

     

     上例可见一般情况下,委托的应用,即在对应一个方法使用委托的情况下。

    注意:

     对于式:DelegateShowInt showInt = new DelegateShowInt(ShowData) 可以认为是所有委托都有的“构造函数”形式——其参数是函数引用。

    多播委托:

    前面例子中是最为简单、常用的一种形式(一个委托实例,调用一个方法)。因为我们可以实例化一个或多个某种委托的实例,另一方面,我们也可以让一个委托调用多个方法,这里称作多播委托。

    //Description: 通过建立多播委托实例,表现多播委托的定义及使用方法

    //CopyRight: http://www.cnblogs.com/yangmingming

    //Notes: 为简便,将委托的定义,和验证代码放于一起

    namespace DelegateDemo
    {
        
    class Program
        {
            
    //定义委托,注意返回类型为 :void
            public delegate void  DelegateShowInt(int i);
            
    public static  void ShowData(int i)
            {
                Console.WriteLine(
    "ShowData is {0}", i);
            }
            
    public static void  ShowData2(int i)
            {
                Console.WriteLine (
    "ShowData2 is {0}",i+1);
            }


            
    static void Main(string[] args)
            {
                
    //多播委托的使用方法:+=
                DelegateShowInt showInt = new DelegateShowInt(ShowData);
                showInt 
    +=new DelegateShowInt( ShowData2);
                showInt(
    1);
                showInt(
    2);
            }
        }
    }

     而对应的调试结果为:

     

     通过实例,可见我们对委托对象,实现了多个方法的绑定。

    注意: 之所以要求多播委托的返回类型为void,是因为如果有返回值,将没有意义,起到作用的只有最后一个被调用的函数而已!

    事件概述:

       有了前述对委托的讲述,为这里讲解事件做好了铺垫。事件,其在Windows Form中得到极大应用,这里举一最常见的点击按钮,触发事件的例子。

     1 private void InitializeComponent()
     2         {
     3             this.buttonOne = new System.Windows.Forms.Button();
     4             this.SuspendLayout();
     5             // 
     6             // buttonOne
     7             // 
     8             this.buttonOne.Location = new System.Drawing.Point(24137);
     9             this.buttonOne.Name = "buttonOne";
    10             this.buttonOne.Size = new System.Drawing.Size(7523);
    11             this.buttonOne.TabIndex = 0;
    12             this.buttonOne.Text = "button";
    13             this.buttonOne.UseVisualStyleBackColor = true;
    14             this.buttonOne.Click += new System.EventHandler(this.button_Click);
    15             // 
    16             // Form1
    17             // 
    18             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
    19             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
    20             this.ClientSize = new System.Drawing.Size(292266);
    21             this.Controls.Add(this.buttonOne);
    22             this.Name = "Form1";
    23             this.Text = "Form1";
    24             this.ResumeLayout(false);
    25 
    26         }

    可见在14行,对对象buttonOne的事件Click赋予了委托,该委托指定了形式:

    private void button_Click(object sender, EventArgs e)
            {

            }

     对应类型的参数含有object和EventArgs,并且返回为void形式。这样事件就与委托相联系上。

    同样可以实现不同对象的事件,对应同一委托情形:

     this.buttonOne.Click += new System.EventHandler(this.button_Click);
                
    this.buttonTwo.Click += new System.EventHandler(this.button_Click);


    综上可见:

    1.委托,作为一种特殊的类型,雷同于定义一个类(实际上,后台即是将其作为类处理)

    2.事件,规范了其类型(委托类型);

    综上之,之所以接口不能包含委托,而接口为什么可以包含事件呢?

    因为委托是一种与类同等地位的特殊类型(类的实例是存储数据的托管内存空间,而委托则只有方法地址而已),故接口不能包含委托;事件从地位上不与类、接口同一层次,事件的独特机制使得事件可以是一个接口的标准组成部分,只要继承的类包含一个public事件成员;(看样要想说明白,还要将事件讲述更清楚些,呵呵~)


    附:附加版

        如上文所述,事件一般都是应用在诸如Windows form这样的事件驱动环境中。具体例子可见上述按钮触发事件自动生成的代码。然而对事件而言,我们自己也可以定义事件的,见下例:

     1 //Description: 通过自定义事件,了解自定义事件使用方法
     2 
     3 //CopyRight: http://www.cnblogs.com/yangmingming
     4 
     5 //Notes: 为简便,将自定义事件的定义,和验证代码放于一起
     6 
     7 
     8 namespace SelfDefinitionEvent
     9 {
    10     //先定义一委托类型
    11     public delegate void DelegateDog( );
    12     
    13 
    14     public class Dog
    15     {
    16         //定义事件类型,其类型对应于自定义委托类型
    17         public event DelegateDog EventDog;
    18         private string name;
    19         public string Name
    20         {
    21             get { return name; }
    22             set { name = value; }
    23         }
    24         public void Invoked()
    25         {
    26             //使用事件方法:像使用其对应方法一样使用
    27             EventDog();
    28         }
    29     }
    30     class Program
    31     {
    32         public static void Response()
    33         {
    34             Console.WriteLine("It will attack human.");
    35         }
    36         static void Main(string[] args)
    37         {
    38             Dog dog = new Dog();
    39             dog.Name = "Jack";
    40             //对事件的操作,使之与对应委托相关
    41             dog.EventDog += new DelegateDog(Response);
    42             dog.Invoked();
    43             
    44         }
    45     }
    46 }
    47 

    最后结果为:

     

       

     从上例,可以看到自定义事件的从定义、赋值、使用等个部分的方法,也可以加深对事件的了解。

    索引器的概述:

          索引器(indexer)是类中相当特殊的一类成员,其功能类似于数组,对象可以按照索引编号对对象中的集合进行存(set)和取(get)操作。这是雷同于数组的方面。然而在类中声明、定义方式,却更相似于属性——同样采用get 和 set访问器(accessor)。 在此只是简要说明,下面用一代码具体实现之,同时也有了索引器的初步理解:

     1 //Description: 通过建立类Student,以及引用它的类Team,表现索引的使用方法
     2 
     3 //CopyRight: http://www.cnblogs.com/yangmingming
     4 
     5 //Notes: 为简便,将类Student和类Team,以及验证代码放于一起。同时没有对索引中的索引编号越界做判断
     6 namespace IndexerAtClassDemo
     7 {
     8     //对类Student 的定义,以便于类Team 中应用
     9     public class Student
    10     {
    11         private string name;
    12         public string Name
    13         {
    14             get { return name; }
    15             set { name = value; }
    16         }
    17     }
    18 
    19     //类Team的定义
    20     public class Team
    21     {
    22         //先声明一Student 数组
    23         private Student[] student = new Student[10];
    24 
    25         //通过索引编号来引用
    26         public Student this[int i]
    27         {
    28             get { return student[i]; }
    29             set { student[i] = value; }
    30         }
    31 
    32         //通过关键字来引用
    33         public Student this[string n]
    34         {
    35             get
    36             {
    37                 foreach (Student s in student)
    38                 {
    39                     if (s.Name == n)
    40                         return s;
    41                 }
    42                 return null;
    43             }
    44         }
    45 
    46     }
    47     public class Program
    48     {
    49         static void Main(string[] args)
    50         {
    51             //声明两个Student对象
    52             Student s1 = new Student();
    53             s1.Name = "Yangmingming";
    54             Student s2 = new Student();
    55             s2.Name = "Youngman";
    56 
    57             //声明Team对象
    58             Team team = new Team();
    59             team[0= s1;
    60             team[1= s2;
    61 
    62             //用索引号和关键字作为条件的,对象索引引用
    63             Console.WriteLine("The name of s1 and s2 is {0},{1}",team[0].Name ,team [1].Name);
    64             Console.WriteLine("The name of s1 and s2 is {0},{1}",team ["Yangmingming"].Name,team ["Youngman"].Name );
    65 
    66         }
    67     }
    68 }
    69 

     

      两条输出语句,实现了相同的输出结果,为:

     

     从上基本了解到了索引的应用方法,而此时越发的感觉索引,同数组,和属性,有点难以区分。他们的相同点区别大至如下:

    1. 索引器,和属性一样,内部实现了get 和set 访问器;

    2.在功能上,索引器对于对象而言,实现了数组的功效,同时索引器比数组范围更大:在实现了索引编号访问同时,还实现了关键字索引引用的效果;

    3.所有索引器都使用 this 关键字来定义 ;

    4.索引器不可以像属性一样,定义为 Static 类型;等等;

    索引器在接口中的实现:

        前已概述了索引器的基本概念,以及使用方法,现在讨论索引器在接口中的实现方法。由前对接口中属性的实现方法而自然而然联想,我们有理由相信索引器在接口中应该实现为与属性相似的方法,事实的确如此,见如下代码:

     1 //Description: 通过建立接口IShowInt,以及引用它的类CShowInt,表现索引的接口实现方法
     2   
     3  //CopyRight: http://www.cnblogs.com/yangmingming
     4  
     5 //Notes: 为简便,将接口IShowInt和类CShowInt,以及验证代码放于一起。
     6 namespace IndexAtInterfaceDemo
     7 {
     8     //在接口上的索引器定义(indexer)
     9     public interface IShowInt
    10     {
    11 
    12         int this[int index]
    13         {
    14             get;
    15             set;
    16         }
    17     }
    18 
    19     //在实现接口的类上,定义索引器(indexer)
    20     public class CShowInt
    21     {
    22         private int[] arr = new int[100];
    23 
    24         public int this[int index]
    25         {
    26             get
    27             {
    28                 //确定集合 边界 问题
    29                 if (index >= 0 && index <= 99)
    30                 {
    31                     return arr[index];
    32                 }
    33                 return 0;
    34             }
    35             set
    36             {
    37                 if (index >= 0 && index <= 99)
    38                     arr[index] = value;
    39             }
    40         }
    41     }
    42    public  class Program
    43     {
    44         static void Main(string[] args)
    45         {
    46             CShowInt cShowInt = new CShowInt();
    47 
    48             //对对象使用其中索引器
    49             cShowInt[0= 1;
    50             cShowInt[1= 2;
    51 
    52             Console.WriteLine("The int of the Class CShowInt by index is {0},{1}",cShowInt [0],cShowInt [1]);
    53 
    54         }
    55     }
    56 }


    其实现结果,如下图示:

     

     即实现了接口中索引器的实现。

    综述,经过这三篇关于接口中包含内容的讨论,进一步加深了对接口的理解,同时也 进一步学习委托与事件、索引器等。

  • 相关阅读:
    C语言单链表创建,插入,删除
    Java乔晓松spring构造函数的注入以及null的注入
    sentilib_语料库项目_search模块的实现
    spring入门(6)set方法注入依赖之null的注入
    Java乔晓松使用Filter过滤器清除网页缓存
    漂亮的弹框
    C#判断各种字符串(如手机号)
    视频数字水印
    数据校验
    SVN 常见问题操作总结
  • 原文地址:https://www.cnblogs.com/malaikuangren/p/1701847.html
Copyright © 2011-2022 走看看