黑马程序员:C#基础篇(二)

来源:互联网 发布:51返利网 知乎 编辑:程序博客网 时间:2024/06/06 01:34

---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------

1、静态类和静态类成员

静态类和静态类成员用于创建不必依赖类的实例就能够访问的数据和行为。

1.1当类中没有依赖对象标志的数据或行为时,可以使用静态类

静态类的特点:(1)仅包含静态成员(2)不能被实例化(3)是密封的(4)不能包含实例构造函数,可以有静态构造函数对静态字段初始化。(5)不可被继承

1.2静态成员

静态成员与实例成员不同,静态成员是公共的,属于这个类型,不会根据此类型的对象状态的变化而变化。另外注意一点,静态方法和属性只能访问静态字段和静态事件。静态成员在第一次访问且在任何静态构造函数(如果调用了的话)之前初始化。

2、构造函数和析构函数

2.1使用实例构造函数

任何时候只要创建类或者结构的实例,就会调用其构造函数,类和结构可以有多个接受不同参数的构造函数(即重载)。

通过设置构造函数为私有的,还可以限制实例化,如单例模式:

    class MyClass    {        static MyClass instance;        public static MyClass Instance        {            get             {                if (instance == null)                {                    instance = new MyClass();                }                return instance;            }        }        private MyClass() { }    }

结构类型的构造函数与类的构造函数类似,但是结构不能包含显式默认构造函数(非静态类如果没有声明构造函数,C#编译器会自动提供一个无参公共的默认构造函数),编译器会会为结构提供一个构造函数,用于初始化结构中的字段为默认值。当结构用new运算符时,其实就是调用了其构造函数,而不是像类一样,在堆中分配了地址。

int i =new int();//合法,i=0Console.Write(i);int i;Console.Write(i);//不合法

但是对于值类型来说,调用构造函数不是必须的,可以直接为其赋值。

带参数的构造函数必须通过new语句或者base语句来调用,如:

    public class MyBase    {        public int value;        public MyBase(int value)        { this.value = value; }        public MyBase(int value, int x)        { this.value = value * x; }    }    public class MySub : MyBase    {        public MySub(int value)            : base(value)        {...}    }

在派生类中,如果不使用base关键字来显式调用基类构造函数,且基类提供了默认构造函数,则将隐式调用默认构造函数,下面的构造函数声明在效果上相同:

先执行基类构造函数,再执行自身构造函数

public MySub(int value)            : base()        {...}public MySub(int value)        {...}

如果基类没有提供默认(即无参)构造函数,派生类必须使用base显式调用基类构造函数。

构造函数还可以使用this关键字调用同一对象中德另一个构造函数,例如,接上例:

        public MyBase(int value, int x):this(value*x)        { this.value = value * x; }

this关键字会导致public MyBase(int value){...}被调用。

2.2静态构造函数

静态构造函数用于初始化静态数据,或执行仅需一次的特定操作。

静态构造函数有以下特点:(1)没有访问修饰符,也没有参数(2)在创建第一个实例或者引用任何静态成员之前,会自动调用静态构造函数(3)无法直接调用静态构造函数(4)在程序中用户无法控制何时执行静态构造函数

静态构造函数额典型用途是当类使用日志文件时用其在日志文件中写入项。

    public class SCtor    {        static SCtor()        { Console.WriteLine("我是静态构造函数"); }        public static void Fun()        { Console.WriteLine("我是一个静态方法"); }        static void Main()        {            SCtor.Fun();            Console.ReadKey();        }    }

2.3析构函数

析构函数用于析构类的实例,特点如下:

(1)只能对类使用析构函数,不能再结构中定义

(2)一个类只能有一个析构函数

(3)析构函数不能被继承,也不能重载

(4)无法调用析构函数,它们是自动被调用的

(5)~后即没有修饰符也没有参数

class Car{      ~Car(){...}}

析构函数的调用由垃圾回收器决定,垃圾回收器如果认为某个对象符合析构,则调用析构函数回收用来存储此对象的内存。程序退出时也会调用析构函数。

C#不需要过多的进行内存管理,垃圾回收器(GC)会隐式地管理对象的内存分配和释放。但是当应用程序用到窗口、文件、网络连接这类非托管资源时,应当使用析构函数释放资源,可以显式地通过实现IDisposable接口的Dispose方法来完成。

下例大概演示了析构函数的调用:

    class First    { ~First() { Console.WriteLine("First 析构"); } }    class Second : First    { ~Second() { Console.WriteLine("Second 析构"); } }    class Third : Second    { ~Third() { Console.WriteLine("Third 析构"); } }    class Program    {        static void Main(string[] args)        {            Third t = new Third();        }    }

当程序结束时,符合析构,这三个类的析构函数会按照派生程度大的到派生程度小的依次调用。

3、方法

方法的语法就不赘述了,方法的参数分为引用传递和值传递,通过参数前加ref或者out关键字可以使参数按引用传递,out参数可以在方法体中赋值并且传到方法体外,ref参数需要在方法体外赋值。

3.1返回值

方法的返回类型如果为空,可以不用return返回,当然,也可以直接return;

方法的返回类型如果不为空,不管代码的分流执行如果,必须确保有对应的返回值(可以是存在隐式转换的类型)。

        static object Fun(int i)        {            if (i > 0) { return i; }            if (i < 0) { return -i;}            if (i == 0) { return null; }            return null;        }


3.2重载

每个类型成员都有一个唯一的签名,由方法名和参数列表(参数的类型和顺序)组成。只要签名不同,即可在一个类型内定义具有同名的多种方法,这就叫重载。(包括从父类继承的方法)

3.2.1方法的重载问题

当重载中方法的参数类型存在了隐私转换或者继承关系,那么当使用null调用的时候到底调哪个呢?
首先,肯定是存在一个优先级的,将会通过隐士类型转换的方向来决定调用哪个。

 class A { } class B : A { } //B 可以直接隐士转换成A(里氏转换原则) //当有以下2个重载方法时:public void Method(A obj ){ … }//重载Apublic void Method(B obj ){ … }//重载B那么Method( null )一定会调用B方法。
还有几个问题:1、对于可空类型来说,如果B可以隐士转换到A,那么B?到A?也存在这种关系。对于数组来说,B[]也可以隐士转换到A[]。2、如果这种隐士转换是双向的,比如自定义转换:
     class A     {         private int num=0;         public A(int num)         { this.num = num; }         //声明从A到B的隐士转换        public static implicit operator B(A a)         {             return new B(a.num);         }     }     class B      {         private int num=0;         public B(int num)         { this.num = num; }         //声明从B到A的隐士转换        public static implicit operator A(B b)         {             return new A(b.num);         }     }
当这种隐士转换是双向的时候,使用null调用Method( null )会发生编译错误。广的来说,只要多个类型之间存在一个闭环的隐士类型转换(A-->B  B-->C C-->A),如果这三个类型的重载都存在的话,那么还是会发生编译错误。
public void Method(A obj ){ … }//重载Apublic void Method(B obj ){ … }//重载Bpublic void Method(C obj ){ … }//重载C //A-->B  B-->C C-->A,用null调用Method( null )会发生编译错误

3.2.1运算符的重载

C#允许用户定义的类型通过使用operator关键字定义(静态)成员行为来重载运算符。

可以重载的一元运算符:   +、-、!、~、++、--、true和false

可以重载的二进制运算符:+、-、*、/、%、&、|、^、<<和>>

可以重载比较运算符:       ==、!=、<、>、<=和>=    (必须成对重载)

 

条件逻辑运算符不能被重载 :&&、||   (但是可以使用能够重载的&和|进行计算)

数组索引运算符不能被重载:[]     (可以定义索引器)

转换运算符不能被重载:()      (但可以定义新的转换运算符(参考自定义转换))

赋值运算符不能被重载:+=、-=、*=、/=、%=、&=、|=、^=、<<=、>>=、包括=

还有一些也不能被重载:.、?:、->

比较运算符必须成对重载,即如果重载了==,也必须重载!=。

 

举例如下,重载了+和*以执行小数加法和乘法,同时提供到双精度浮点数的隐式转换:

    class Oper    {        int x, y;        public Oper(int x,int y)        {            this.x = x;            this.y = y;        }        //重载+运算符        public static Oper operator + (Oper a,Oper b)        {            return new Oper(a.x*b.y+b.x*a.y,a.x+b.y);        }        //重载*运算符        public static Oper operator *(Oper a, Oper b)        {            return new Oper(a.x * b.x, a.y * b.y);        }        //自定义转换        public static implicit operator double(Oper o)        {            return (double)o.x / o.y;        }    }    class Test    {        static void Main(string[] args)        {            Oper a = new Oper(1, 2);            Oper b = new Oper(3, 7);            Oper c = new Oper(5, 8);            Console.WriteLine(a * b + c);//8.54545454545455            Console.ReadKey();        }    }

4、字段及属性

4.1字段

字段一般用于存储数据,属性是对字段的封装。字段的初始值设定项不能引用其他实例字段。字段可以声明为readonly,只读字段在初始化期间或在构造函数中赋值。

静态的只读字段相当于常量,但C#编译器不能在编译时访问,只能在运行时访问。

4.2属性

属性其实是称为“访问器”的特殊方法,可以说是只读的,只写的,可读可写的。

(1)只读访问器

        private string str;        public string Str        {            get { return str; }        }

get访问器必须返回一个值

(2)只写访问器

        private string str;        public string Str        {            set { str = value; }        }

set访问器包含了一个隐式参数value,表示赋给该属性的值

(3)读写访问器

        private string str;        public string Str        {            get { return str; }            set { str = value; }        }

同一属性的get和set访问器可能有不同的访问级别,如get可能是public,允许外界的只读访问,而set是protected或者private。但是也有很多要注意的:

(1)访问器的可访问级别不能比属性或索引器本身的可访问级别高

(2)只有当为读写属性或索引时,才能对访问器使用访问修饰符,并且只允许对一个访问器使用修饰符

(3)不能对接口或者显式实现的接口成员使用访问器修饰符

(4)如果具有属性或者索引有override修饰符(即是重写的),则访问器修饰符必须与重写的访问器(如果有)匹配

在重写属性或索引器时被重写的访问器对重写代码而言,必须是可访问的,如下:

    abstract class Test    {        public abstract object Attribute        { get; protected set; }    }    class Sub : Test    {        public override object Attribute        {            get            {                return null;            }            protected set            {                Console.WriteLine(value);            }        }    }

 

使用访问器实现接口时该访问器不能具有访问修饰符(接口成员默认是公共的,那么从上面可知,重写后的访问器也必须为公共的),不过一个访问器实现接口,另一个却可以有访问修饰符,如下

    public interface IOverrideAttribute    {        object TestAttribute { get; }    }    public class MySub : IOverrideAttribute    {        public object TestAttribute        {            get { return 0; }            protected set { }        }    }

5、索引器

索引器允许类或结构的实例通过[params](与数组访问元素的方式类似)来索引。与属性类似,但是其访问器采用了参数。

get访问器指向该索引的值,set访问器设置指向该索引的值,可以被重载,并且可以有多个形参。

声明类或结构上的索引要用this关键字:

        public object this[int i]        { get; set; }

索引器的签名由其形参的数量和类型包括顺序组成,不包括索引器类型或形参名。要想对索引器重载,必须有不同的签名:

    public class Program    {        int[] nums = new int[5];        string[] strs = new string[10];        public int this[int i]        {            get { return nums[i]; }            set { nums[i] = value; }        }        public int this[string str]        {            get { return str.Length; }            set { Console.WriteLine(value); }        }     }

 

---------------------- ASP.Net+Android+IOS开发、.Net培训、期待与您交流! ----------------------

原创粉丝点击