一些基本本概念

来源:互联网 发布:淘宝床垫 编辑:程序博客网 时间:2024/04/29 21:26

有些概念是网上收集,如有错误或更好的说明请留言!

 

一、组件(Component)和控件(Control)

二、UserControl和Control之间的区别

三、委托和事件简介

四、静态方法和动态方法的区别

五、抽象类与接口

六、装箱(Boxing)和拆箱(Unboxing)

 

一、组件(Component)和控件(Control)

1、Component在Run Time时不能呈现UI,而Control可以在Run Time时呈现UI(也不完全是)。

2、Component是贴在容器Container上的,而Control则是贴在Windows Form或者Web Form上的。

 举例来说,SqlCommand是个Component,DataGrid则是一个Control。

 

二、UserControl和Control之间的区别

简单的说:UserControl比Control更高级,提供了更多的高级特性,Control 类只提供控件所需的所有基本功能(包括鼠标和键盘处理事件),但不提供可视化的UI,所以说从Control派生的话,用户必须override OnPaint,UserControl相比Control为我们提供了更多的特性,但是UserControl也就失去了更多的灵活性。

 

三、委托和事件简介

委托是C#中的一种引用类型,类似于C/C++中的函数指针。与函数指针不同的是,委托是面向对象、类型安全的,而且委托可以引用静态方法和实例方法,而函数指针只能引用静态函数。委托主要用于 .NET Framework 中的事件处理程序和回调函数。

一个委托可以看作一个特殊的类,因而它的定义可以像常规类一样放在同样的位置。与其他类一样,委托必须先定义以后,再实例化。与类不同的是,实例化的委托没有与之相应的术语(类的实例化称作对象),作为区分我们将实例化的委托称为委托实例。

我们再谈谈为什么委托是类型安全的。C#中的委托和指针不一样,指针不通过MSIL而是直接和内存打交道,这也是指针不安全的原因所在,当然也是采用指针能够提高程序运行速度的缘故;委托不与内存打交道,而是把这一工作交给CLR去完成。CLR无法阻止将不安全的代码调用到本机(非托管)代码中或执行恶意操作。然而当代码是类型安全时,CLR的安全性强制机制确保代码不会访问本机代码,除非它有访问本机代码的权限。

委托派生于基类System.Delegate,不过委托的定义和常规类的定义方法不太一样。委托的定义通过关键字delegate来定义:

 public delegate int myDelegate(int x, int y);

上面的代码定义了一个新委托,它可以封装任何返回为int,带有两个int类型参数的方法。任何一个方法无论是实例方法还是静态方法,只要他们的签名(参数类型在一个方法中的顺序)和定义的委托是一样的,都可以把他们封装到委托中去。这种签名方法正是保证委托是类型安全的手段之一。

产生委托实例和产生类实例(对象)差不多,假如我们有如下的方法:

        public int sub(int x, int y)

        {

            return (x + y);

        }

我们就可以使用如下的代码得到一个委托实例:

     myDelegate calculatin = new myDelegate(sub);

接下来我们就可以直接使用calculation调用sub方法了:

calculation(10,3);

下面我们将用委托重写上面的一个程序来看一下在C#中如何通过委托实现由函数指针实现的功能:

    class MathClass

    {

        public static int max(int a, int b)

        {

            return (a > b ? a : b);

        }

        public static int min(int a, int b)

        {

            return (a < b ? a : b);

        }

        public static int sub(int a, int b)

        {

            return (a + b);

        }

        public static int minus(int a, int b)

        {

            return (a - b);

        }

    }

 

    class Handler

    {

        private delegate int Calculation(int a, int b);

        private static Calculation[] myCalculation = new Calculation[2];

       

        public static void EventHandler(int i, int a, int b)

        {

            switch (i)

            {

                case 1:

                    myCalculation[0] = new Calculation(MathClass.max);

                    myCalculation[1] = new Calculation(MathClass.min);

                    Console.WriteLine(myCalculation[0](a, b));

                    Console.WriteLine(myCalculation[1](a, b));

                    break;

                case 2:

                    myCalculation[0] = new Calculation(MathClass.sub);

                    myCalculation[1] = new Calculation(MathClass.minus);

                    Console.WriteLine(myCalculation[0](a, b));

                    Console.WriteLine(myCalculation[1](a, b));

                    break;

                default:

                    return;

            }

        }

    }

 

    class Test

    {

        static void Main()

        {

            Handler.EventHandler(1, 10, 3);

            Handler.EventHandler(2, 10, 3);

        }

    }

我们还可以声明一个委托数组,就像声明一个对象数组一样,上面的例子中就使用到了委托数组;一个委托还可以封装多个方法(多路广播委托,经常与事件处理程序结合使用),只要这些方法的签名是正确的。多路广播委托的返回值一般为void,这是因为一个委托只能有一个返回值,如果一个返回值不为void的委托封装了多个方法时,只能得到最后封装的方法的返回值,这可能和用户初衷不一致,同时也会给管理带来不方便。如果你想通过委托返回多个值,最好是使用委托数组,让每个委托封装一个方法,各自返回一个值。

 

事件:C#中,委托的最基本的一个用处就是用于事件处理。事件是对象发送的消息,以发信号通知操作的发生,通俗一点讲,事件就是程序中产生了一件需要处理的信号。

事件的定义用关键字event声明,不过声明事件之前必须存在一个多路广播委托:

     public delegate void Calculate(int x, int y);//返回值为void的委托自动成为多路广播委托

     public event calculate OnCalculate;

从上面的委托实例和上面的事件的声明可以看出,事件的声明仅仅是比委托实例的声明多了个关键字event,事实上事件可以看作是一个为事件处理过程定制的多路广播委托。因此,定义了事件后,我们就可以通过向事件中操作符+=添加方法实现事件的预定或者是通过-=取消一个事件,这些都与委托实例的处理是相同的。与委托实例不同的是,操作符=对于事件是无效的,即

    OnCalculate=new calculate(sub) ;//无效

只是因为上面的语句会删除由OnCalculate封装的所有其他方法,指封装了由此语句指定的唯一方法,而且一个预定可以删除其他所有方法,这会导致混乱。

 

四、静态方法和动态方法的区别

1.静态的方法在整个应用程序其间存储在内存中,速度快,但占用内存. 

    class A

    {

        public static string b()

        {

            return "Hello";

        }

    }  

用法:  A.b();      //调用方便 

 

2.动态的方法在先声明类实例才能调用类中的方法. 

    class A

    {

        public string b()

        {

            return "Hello";

        }

    }  

用法:   A  a  =  new  a();      a.b();     

 

3.一般使用频繁的方法用静态方法,用的少的方法用动态的。静态的速度快,占内存。动态的速度相对慢些,但调用完后,立即释放类,可以节省内存,可以根据自己的需要选择是用动态方法还是静态方法。

 

五、装箱(Boxing)和拆箱(Unboxing) 

C#数据类型可分为在堆栈上分配内存的值类型和托管堆上分配内存的引用类型。  Boxing:是把值类型转换为引用类型;Unboxing:是把引用类型转换成值类型。 

  int i = 10;

  object o = i;    // Boxing:系统会在托管堆上为o分配一个新的空间

  int j = (int)o;  //Unboxing:系统在堆栈上分配一个临时空间(这里给了j),并把o的//值放进去,改变o或j并不会影响另一个的值。

六、抽象类与接口

   接口是对象属性和方法的描述(但不包括他们具体实现),比如Int32类型实现了IConvertible接口,就说明Int32具有IConvertible接口所描述的ToDouble()方法。但IConvertible并没有给出实现ToDouble()的内容,需要由Int32类型自己实现。接口用于实现多态。比如Int32,Int64和Single类型都实现了IConvertible借口,那么就说明他们一定都具有ToDouble()方法。所以,定义一个变量是IConvertible类型:

IConvertible c;   

然后,无论是给c赋任何实现IConvertible类型的变量,我都能够保证,c至少有一个ToDouble()方法可用,而不用关心c具体是什么类型的变量。如   

int  i  =  3;      //Int32   

long  j  =  6L;   //Int64   

float  k  =  4F;  //Single    

 

c  =  i;   

c.ToDouble();   

 

c  =  j;   

c.ToDouble();   

 

c  =  k;   

c.ToDouble();   

都不会发生错误。   

 

实现接口的类可以显式实现该接口的成员。当显式实现某成员时,不能通过类实例访问该成员,而只能通过该接口的实例访问该成员。  接口的应用大多数是在Design  Pattern时才用到。

抽象类,从多个对象中抽出来的“共性”,而他的后代,既有共性、又有特性。例如:“图形”是抽象的,没有形状,由点线组成;正方形、圆形是他的派生,可以是对象。