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 }


    其实现结果,如下图示:

     

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

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

  • 相关阅读:
    HDU 4069 Squiggly Sudoku
    SPOJ 1771 Yet Another NQueen Problem
    POJ 3469 Dual Core CPU
    CF 118E Bertown roads
    URAL 1664 Pipeline Transportation
    POJ 3076 Sudoku
    UVA 10330 Power Transmission
    HDU 1426 Sudoku Killer
    POJ 3074 Sudoku
    HDU 3315 My Brute
  • 原文地址:https://www.cnblogs.com/malaikuangren/p/1701847.html
Copyright © 2011-2022 走看看