zoukankan      html  css  js  c++  java
  • 第五章 类

    1 面向对象程序设计基础

    如果一个软件系统是使用这4个概念来设计和实现的,就认为这个软件系统是面向对象的

    1.1 对象

    1.2 类

    类是一组具有相同数据结构和相同操作的对象的集合

    1.3 继承

    继承是以现有类型定义为基础创建新类型的技术。

    1.4 消息通信

    对象与对象之间,通过消息进行通信。

    2 类的生命周期

    2.1 构造函数

    对象的初始化通常由类的构造函数来完成。

    a、构造函数的名称与类名相同

    b、构造函数不声明返回类型

    c、构造函数通常是公有的,如果声明为保护的或私有的,则该构造函数不能用于类的实例化

    d、构造函数的代码中通常中只进行对象初始化工作,而不应执行其它操作

    e、构造函数在创建对象时被自动调用,不能像其它方法那样显式的调用

        public class ConstructSample
        {
            public static void Main()
            {
            Person p = new Person();
            Console.WriteLine(p.m_name);
            }
        }
    
        public class Person
        {
            public string m_name;
            protected int m_age;
            protected bool m_gender;
    
            //构造函数
            public Person()
            {
                m_name = "Unknown";
                m_age = 0;
                m_gender = false;
            }
        }

    带参数的构造函数(构造函数的重载)

        public class Person
        {
            public string m_name;
            protected int m_age;
            protected bool m_gender;
    
            //构造函数
            public Person()
            {
                m_name = "Unknown";
                m_age = 0;
                m_gender = false;
            }
            //构造函数重载
            public Person(string Name)
            {
                m_name = Name;
                m_age = 0;
                m_gender = true;
            }
        }

    2.2 静态构造函数

    如果使用了关键字static来定义构造函数,那么该构造函数就属于类而不是类的实例所有。

    在程序第一次用到某个类时,类的静态构造函数自动被调用,而且是仅此一次。

    静态构造函数通常用于对类的静态字段进行初始化

    静态构造函数不使用任何访问限制修饰符。

        public class StaticConstructSample
        {
            public static void Main()
            {
                Person p1 = new Person("Mike");
                Person p2 = new Person("John");
                Person p3 = new Person("Mary");
            }
        }
    
        public class Person
        {
            public string m_name;
            public static int m_object=0;
            public static int m_classes = -1;
    
            //构造函数
            public Person(string Name)
            {
                m_name = Name;
                Console.WriteLine(m_name);
                Console.WriteLine("Object before:{0}", m_object);
                m_object++;
                Console.WriteLine("Object after:{0}", m_object);
            }
            //静态构造函数
            public Person()
            {
                Console.WriteLine("Classes before:{0}", m_classes);
                m_classes++;
                Console.WriteLine("Classes after:{0}", m_classes);
            }
        }

    程序输出结果

    Mike
    Object before:0
    Object after:1
    John
    Object before:1
    Object after:2
    Mary
    Object before:2
    Object after:3
    请按任意键继续. . .

    2.3 析构函数

    对象使用完毕后,释放对象时就会自动调用类的析构函数

    --析构函数的名称与类名相同,但在名称前面加了一个符号"~"

    --析构函数不接受任何参数,也不返回任何值

    --析构函数不能使用任何访问限制修饰符

    --析构函数中的代码通常只进行销毁对象的工作,而不应执行其它的操作

    --析构函数不能被继承,也不能被显示的调用

    --如果类中没有显式的定义一个析构函数,编译时也会生成一个默认的析构函数,其执行代码为空。

    --不存在静态的析构函数

    3 属性

    --为了实现良好的数据封装和数据隐藏,C#为类提供了属性(Property)成员。

    --属性是对字段的扩展,它通过属性访问函数来控制对字段的访问。

    --属性访问函数包括get访问函数和set访问函数,分别用于对字段的读取和修改。

    例如Person类中可以使用Name属性来封装对私有字段name的访问

    public class Person
    {
        //字段
        private string name;
        //属性
        public string Name
        {
            get
            {
                return name;
            }
            set
            {
                name=value;
            }
        }
    }
    
    public static void Main()
    {
        person p1=new Person();
        p1.Name="Mike";//set
        Console.WriteLine(p1.Name);//get
    }    

    --定义属性时可以只声明一个访问函数。如果只有get,则表明属性的值不能为修改;如果只有set,则表明属性的值只能写入。

    --和方法一样,属性可以声明为静态的,也可以使用各种访问限制修饰符

    --属性可以作为特殊的方法来使用,而不必和字段一一对应

    public class Person
    {
        private DateTime birthday;
    
        public int Age
        {
            get
            {
                return DataTime.Now.Year-birthday.year;
            }
        }
    }

    4 索引函数

    --索引函数对属性做了进一步的扩展,它能够以数组的方式来控制对多个变量的读写访问。

    --和属性一样,索引函数可以被看作是get访问函数和set访问函数的组合,不同之处在于:

      --索引函数以this关键字加数组形式的下标进行定义,并通过数组形式的下标进行访问;

      --索引函数的get和set带用参数(一般为整型或字符串类型)

      --索引函数不能是静态的

      --和属性类似,索引函数的get和set访问函数中可以增加控制代码

    下面的代码示例了一个Per    public class PersonTable

        {
            private Person[] m_list;
    
            public int Length
            {
                get
                {
                    return m_list.Length;
                }
            }
    
            //索引函数
            public Person this[int index]
            {
                get
                {
             if(index>=0 && index <Length)   
    return m_list[index];
             else
                return null;
    }
    set {
             if(index>=0 && index <Length)
       m_list[index]
    = value;
             else
               throw new IndexOutOfRangeException(); } } }

    这样对类的使用和数组的使用就非常类似了

    PersonTable pt = new PersonTable(3);
    pt[1]=new Person("Mike");
    ...
    ...

    索引函数的访问对象不一定要是连续的数据,也可以是多个离散的字段。例如

        public class IndexerSample
        {
            static void Main()
            {
                Person p1 = new Person("李四");
                p1["BusinessPhone"] = "010888888";
                p1["BusinessFax"] = "0108888888";
                p1["MobilePhone"]="13988888888";
                p1.Output();
            }
        }
    
        public class Person
        {
            private string m_name;
            private string m_busiPhone;
            private string m_busiFax;
            private string m_homePhone;
            private string m_mobilePhone;
    
            public string Name
            {
                get
                {
                    return m_name;
                }
                set
                {
                    m_name = value;
                }
            }
    
            //索引函数
            public string this[string Stype]
            {
                get
                {
                    string type=Stype.ToUpper();
                    switch (type)
                    {
                        case "BUSINESSPHONE":
                            return m_busiPhone;
                        case "BUSINESSFAX":
                            return m_busiFax;
                        case "HOMEPHONE":
                            return m_homePhone;
                        case "MOBILEPHONE":
                            return m_mobilePhone;
                        default:
                            return null;
                    }
                }
                set
                {
                    string type=Stype.ToUpper();
                    switch (type)
                    {
                        case "BUSINESSPHONE":
                            m_busiPhone = value;
                            break;
                        case "BUSINESSFAX":
                            m_busiFax = value;
                            break;
                        case "HOMEPHONE":
                            m_homePhone = value;
                            break;
                        case "MOBILEPHONE":
                            m_mobilePhone = value;
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }
                }
            }
    
            //构造函数
            public Person()
            {
            }
    
            public Person(string sName)
            {
                m_name = sName;
            }
    
            //方法
            public void Output()
            {
                Console.WriteLine(m_name);
                Console.WriteLine("商务电话:{0}", m_busiPhone);
                Console.WriteLine("商务传真:{0}", m_busiFax);
                Console.WriteLine("家庭电话:{0}", m_homePhone);
                Console.WriteLine("移动电话:{0}", m_mobilePhone);
            }
        }

    5 事件

    通过事件(Event),对象可以对发生的情况做出反映。

    在C#的事件处理模型中,某个事件发生后,对象通过该事件的代表(delegate)调用适当的事件处理代码,此时代表充当了产生事件的对象与处理事件的方法之间的“中间人”。

    System程序集中定义了一个EventArgs的类,用来封装事件中所包含的数据:此外还定义了一个名为EventHandler的delegate对象,用来作为所有事件的代表,其原型为:

    public delegate void EventHandler(object sender,EventArgs e)

    其中的参数sender表示发生事件的对象,而e表示事件中包含的数据。

    C#中的事件也是一种特殊的方法,它包含一对内部函数add和remove,add函数将代表附加到事件上,而remove函数将移除已附加到事件上的代表。

        public class EventSample
        {
            static void Main()
            {
                Person p1 = new Person("李四");
                p1.Name = "李明";
                Console.WriteLine("当前姓名为:{0}", p1.Name);
            }
        }
    
        public class Person
        {
            //字段
            private string m_name;
            private string m_busiPhone;
            private string m_busiFax;
            private string m_homePhone;
            private string m_mobilePhone;
    
            //属性
            public string Name
            {
                get
                {
                    return m_name;
                }
                set
                {
                    if(m_name!=value)
                    {
                        OnNameChange(this, new EventArgs());
                        m_name = value;
                    }
                }
            }
    
            //代表
            internal EventHandler eh;
    
            //索引函数
            public string this[string Stype]
            {
                get
                {
                    string type=Stype.ToUpper();
                    switch (type)
                    {
                        case "BUSINESSPHONE":
                            return m_busiPhone;
                        case "BUSINESSFAX":
                            return m_busiFax;
                        case "HOMEPHONE":
                            return m_homePhone;
                        case "MOBILEPHONE":
                            return m_mobilePhone;
                        default:
                            return null;
                    }
                }
                set
                {
                    string type=Stype.ToUpper();
                    switch (type)
                    {
                        case "BUSINESSPHONE":
                            m_busiPhone = value;
                            break;
                        case "BUSINESSFAX":
                            m_busiFax = value;
                            break;
                        case "HOMEPHONE":
                            m_homePhone = value;
                            break;
                        case "MOBILEPHONE":
                            m_mobilePhone = value;
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }
                }
            }
    
            //构造函数
            public Person()
            {
            }
    
            public Person(string sName)
            {
                m_name = sName;
            }
    
            //方法
            public void Output()
            {
                Console.WriteLine(m_name);
                Console.WriteLine("商务电话:{0}", m_busiPhone);
                Console.WriteLine("商务传真:{0}", m_busiFax);
                Console.WriteLine("家庭电话:{0}", m_homePhone);
                Console.WriteLine("移动电话:{0}", m_mobilePhone);
            }
    
            //事件
            public event EventHandler NameChange
            {
                add
                {
                    eh += (EventHandler)Delegate.Combine(eh, value);
                }
                remove
                {
                    eh -= (EventHandler)Delegate.Combine(eh, value);
                }
            }
    
            //事件处理方法
            protected void OnNameChange(object sender, EventArgs e)
            {
                Console.WriteLine("人员姓名:{0},已经被修改", m_name);
            }
        }

     这样,每次修改Person的Name属性,将触发NameChange事件,程序输出为:

    人员姓名:李四,已经被修改
    当前姓名为:李明
    请按任意键继续. . .

    C#还提供了事件定义的简写方式,可以不用写出事件的add和remove函数,也不用写出代表的定义。上例中的事件和代表的定义就可以简化为一行代码:

    public event EventHandler NameChange;

    输出效果相同。

    更多的时候,开发人员需要自定义EventArgs的派生类,并在其中自定义事件数据。对Person类进行如下修改:

        public class Person
        {
            //字段
            private string m_name;
            private string m_busiPhone;
            private string m_busiFax;
            private string m_homePhone;
            private string m_mobilePhone;
    
            //属性
            public string Name
            {
                get
                {
                    return m_name;
                }
                set
                {
                    if(m_name!=value)
                    {
                        PersonEventArgs e = new PersonEventArgs();
                        e.m_oldName = m_name;
                        e.m_newName = value;
                        OnNameChange(this, e);
                    }
                }
            }
    
            //索引函数
            public string this[string Stype]
            {
                get
                {
                    string type=Stype.ToUpper();
                    switch (type)
                    {
                        case "BUSINESSPHONE":
                            return m_busiPhone;
                        case "BUSINESSFAX":
                            return m_busiFax;
                        case "HOMEPHONE":
                            return m_homePhone;
                        case "MOBILEPHONE":
                            return m_mobilePhone;
                        default:
                            return null;
                    }
                }
                set
                {
                    string type=Stype.ToUpper();
                    switch (type)
                    {
                        case "BUSINESSPHONE":
                            m_busiPhone = value;
                            break;
                        case "BUSINESSFAX":
                            m_busiFax = value;
                            break;
                        case "HOMEPHONE":
                            m_homePhone = value;
                            break;
                        case "MOBILEPHONE":
                            m_mobilePhone = value;
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }
                }
            }
    
            //构造函数
            public Person()
            {
                NameChange += new EventHandler(OnNameChange);
            }
    
            public Person(string sName)
            {
                m_name = sName;
                NameChange+=new EventHandler(OnNameChange);
            }
    
            //方法
            public void Output()
            {
                Console.WriteLine(m_name);
                Console.WriteLine("商务电话:{0}", m_busiPhone);
                Console.WriteLine("商务传真:{0}", m_busiFax);
                Console.WriteLine("家庭电话:{0}", m_homePhone);
                Console.WriteLine("移动电话:{0}", m_mobilePhone);
            }
    
            //事件
            public event EventHandler NameChange;
    
            //事件参数类
            public class PersonEventArgs : EventArgs
            {
                public string m_oldName;
                public string m_newName;
            }
    
            //事件处理方法
            protected void OnNameChange(object sender, EventArgs e)
            {
                Console.WriteLine("人员姓名:{0},已经被修改为{1}!", ((PersonEventArgs)e).m_oldName,((PersonEventArgs)e).m_newName);
                Console.WriteLine("确定修改(Y/N)?");
                char key = Console.ReadKey().KeyChar;
                Console.WriteLine();
                if (key == 'y' || key == 'Y')
                {
                    m_name = ((PersonEventArgs)e).m_newName;
                    Console.WriteLine("姓名修改成功");
                }
            }
        }

    程序输出结果为:

    人员姓名:李四,已经被修改为李明!
    确定修改(Y/N)?
    y
    姓名修改成功
    当前姓名为:李明
    请按任意键继续. . .

    6 操作符重载

    操作符重载可用于对自定义的数据类型进行基本操作

    允许被重载的操作符包括

    -- 一元操作符:+ - ! ~ ++ -- (T) true false

    -- 二元操作符:+ - * / % & | ^ << >> == != > < >= <=

    考虑到操作符的对称性,下列操作符要求成对重载

    -- 一元操作符:true和false

    -- 二元操作符:==和 !=、>和<、>=和<=

    class Prime
    {
        private uint m_value;
        public Prime(uint iValue)
        {
            m_value=iValue;
        }
    }
    public static uint operator+(Prime p1,Prime P2)
    {
        return p1.m_value+p2.m_value;
    }

    如果添加了以上方法,则p1+p2是合法的,而不用繁琐的写法p1.m_value+p2.m_value;

    对于复合赋值操作符,只要左部操作符是可重载的二元操作符,并且操作符的返回类型也可以隐式的转换成当前类,那么相应的斌值操作符也被隐式重载。例如将上面的重载定义改写为:

    public static Prime operator+(Prime p1,Prime p2)
    {
        return new Prime(p1.m_value+p2.m_value);
    }

    此时p1+=p2这样的表达式也是合法的。

    --被重载的操作符也是一种特殊的方法,且必须被声明为公有的和静态的。

    --重载一元操作符时需提供一个参数,且参数类型应为当前类型,或者是可以隐式转换为当前类型

    --重载二元操作符时需提供两个参数,且至少有一个参数类型为当前类型,或者是可以隐式转换为当前类型

    --对于类型转换操作符(T),在定义重载时需要为T指定一个数据类型。类型转换的重载有一个特殊的地方,就是它不显式的定义返回类型,而认为操作符的返回类型始终为T所代表的类型。此外,需要使用关键字explicit或implicit来指明转换是显式的还是隐式的。

    例如可以定义从Prime类到uint类的隐式转换

    public static implicit operator uint(prime p)
    {
        return p.m_value;
    }

    下面的代码为一个完整的示例:

        public class OperatorSample
        {
            static void Main()
            {
                //输出前20个素数
                Prime p1 = new Prime(2);
                for (int i = 0; i < 20; i++)
                {
                    Console.WriteLine(p1++);
                }
    
                //输出500~1000之间的所有素数
                for (uint j = 500; j < 1000; j++)
                {
                    Prime p2 = new Prime(j);
                    if (p2)
                        Console.WriteLine(p2 + ",");
                }
            }
        }
    
    
        public class Prime
        {
            private uint m_value;
    
            public uint Value
            {
                get
                {
                    return m_value;
                }
            }
    
            //构造函数
            public Prime(uint iValue)
            {
                m_value = iValue;
            }
    
            //重载二元操作符+
            public static uint operator +(Prime p1, Prime p2)
            {
                return p1.m_value + p2.m_value;
            }
    
            //重载二元操作符-
            public static int operator -(Prime p1, Prime p2)
            {
                return (int)(p1.m_value - p2.m_value);
            }
    
            //重载一元操作符++
            public static Prime operator ++(Prime p)
            {
                for (uint i = p.m_value + 1; ; i++)
                {
                    if (IsPrime(i))
                        return new Prime(i);
                }
            }
    
            //重载一元操作符--
            public static Prime operator --(Prime p)
            {
                for (uint i = p.m_value - 1;i>2; i--)
                {
                    if (IsPrime(i))
                        return new Prime(i);
                }
                return new Prime(2);
            }
    
            //重载类型转换操作符(uint)
            public static implicit operator uint(Prime p)
            {
                return p.m_value;
            }
    
            //重载一元操作符true
            public static bool operator true(Prime p)
            {
                return IsPrime(p.m_value);
            }
    
            //重载一元操作符false
            public static bool operator false(Prime p)
            {
                return IsPrime(p.m_value);
            }
    
            //方法,判断一个整数是否为素数
            public static bool IsPrime(uint x)
            {
                for (uint i = 2; i <= x / 2; i++)
                {
                    if (x % i == 0)
                        return false;
                }
                return true;
            }
    
            //重写ToString()
            public override string ToString()
            {
                return m_value.ToString();
            }
        }
    View Code

    7 this关键字

    this用于代一个变量

    --它仅限于在类的非静态方法成员中使用,包括类的构造函数、非静态方法、属性、索引函数及事件

    --在类的构造函数中出现时,它表示正在构造的对象本身

    --在类的方法成员中出现时,它表示调用该方法成员的对象

    上例中的prime类的带参数的构造函数可以写成以下形式

    public Prime(uint iValue)
    {
        this.m_value=iValue;
    }
  • 相关阅读:
    在C#代码中应用Log4Net(二)典型的使用方式
    在C#代码中应用Log4Net(一)简单使用Log4Net
    Windows Azure Active Directory (2) Windows Azure AD基础
    Windows Azure Virtual Network (6) 设置Azure Virtual Machine固定公网IP (Virtual IP Address, VIP) (1)
    Windows Azure Active Directory (1) 前言
    Azure China (6) SAP 应用在华登陆 Windows Azure 公有云
    Microsoft Azure News(3) Azure新的基本实例上线 (Basic Virtual Machine)
    Microsoft Azure News(2) 在Microsoft Azure上运行SAP应用程序
    Microsoft Azure News(1) 新的数据中心Japan East, Japan West and Brazil South
    Windows Azure HandBook (2) Azure China提供的服务
  • 原文地址:https://www.cnblogs.com/boywg/p/4128465.html
Copyright © 2011-2022 走看看