• 第五章 类


    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;
    }
  • 相关阅读:
    团队冲刺阶段二第五次站立会议
    团队冲刺阶段二第四次站立会议
    团队冲刺阶段二第三次站立会议
    团队冲刺阶段二第二次站立会议
    团队冲刺阶段二第一次站立会议
    第一冲刺阶段总结报告
    团队冲刺阶段一第九次站立会议
    团队冲刺阶段一第八次站立会议
    团队冲刺阶段一第七次站立会议
    团队冲刺阶段一第六次站立会议
  • 原文地址:https://www.cnblogs.com/boywg/p/4128465.html
Copyright © 2020-2023  润新知