黑马程序员—C#系列 (四)

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

面向对象(续篇)

1.多态性

多态是面向对象编程中三大机制之一,其原理建立在"从父类继承而来的子类可以转换为其父类"这个规则之上,换句话说,能用父类的地方,就能用该类的子类.当从父类派生了很多子类时,由于每个子类都有其不同的代码实现,所以当用父类来引用这些子类时,同样的操作而可以表现出不同的操作结果,这就是所谓的多态

2.虚方法

1)  虚方法:使用virtual关键字修饰,使用virtual关键字修饰的方法在本类中必须有实现,哪怕是空的{}

virtual public void ShowNationality()
{}或
public virtual void ShowNationality(){} //别忘了大括号

2)  虚方法存在的意义就是为了让子类重写,子类可以重写override,也可以不重写
3)  常用的虚方法:
ToString(),Equals()来自于Object

3.重写

1)  什么是方法重写 override
答:override 方法重写 是指子类继承父类后在子类中有一个与父类中某个方法签名一致,否则重写不成功,其中包括“返回值”,但方法内容可以重新定义的方法,并用override关键字修饰
2)  实现方法重写的方式3个关键字:只有用virtual、abstract、override修饰的方法在继承后子类可以重写
3)  方法重写的意义:
为所有子类定义了某种规范(某种必须有的规范),多态。
这里的规范可以理解为”行为“,就是方法。
以后用到得接口等中提到的规范,其实目的就是让新增加的类必须具有某些方法,这样才能满足我们程序中的要求。(对程序扩展做了一定的约束。)
4)  虚方法存在是为了让子类重写,方法重写是为了多态,多态是为了程序写好以后不需要修改以前的代码,方便程序的可扩展性

4.抽象类

抽象类是为了让子类去重写,扩展他


1) abstract 关键字修饰
2) 抽象类中可以有抽象成员也可以有非抽象成员,有抽象成员的类必须是抽象类,抽象成员在抽象类中不能有任何实现
3) 抽象类中的抽象成员在子类中必须实现(override),除非子类也是抽象类
4) 抽象类可以被继承,但是不能被实例化(就是不能new),其中没有抽象方法的时候就没必要声明为抽象类

错误:base.ShowNationality();//抽象类不能实例化,所以不能通过base来调用

5.如何在程序中运用多态性

练习:形状类Shape(父类),包含Size属性和计算面积getArea()方法。
圆类Circle继承Shape,可以计算圆的面积。正方形类Square继承Shape,可以计算正方形面积。
最终要求能实现:Shape sh=new Circle(5);sh.getArea()能计算出圆的面积。Shape sh=new Square(5);sh.getArea()能计算出正方形的面积。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace 计算面积
{
    class Program
    {
        static void Main(string[] args)
        {


            //Shape shape1 = new Circle(5);
            //Console.WriteLine(shape1.getArea());

            //Shape shape2 = new Square(10, 10);
            //Console.WriteLine(shape2.getArea());

            //计算面积.Circle

            Circle c1 = new Circle(10);
            Console.WriteLine(c1.ToString());
            Console.ReadKey();
        }
    }

    /// <summary>
    /// 形状类
    /// </summary>
    abstract class Shape
    {
        private double _width;

        /// <summary>
        /// 宽
        /// </summary>
        public double Width
        {
            get { return _width; }
            set { _width = value; }
        }

        private double _height;


        /// <summary>
        /// 高
        /// </summary>
        public double Height
        {
            get { return _height; }
            set { _height = value; }
        }

        /// <summary>
        /// 计算指定图形的面积
        /// </summary>
        /// <returns></returns>
        public abstract double getArea();
    }

    /// <summary>
    /// 圆类
    /// </summary>
    class Circle : Shape
    {
        public Circle(double r)
        {
            this.Height = r;
            this.Width = r;
        }

        /// <summary>
        /// 计算圆的面积
        /// </summary>
        /// <returns></returns>
        public override double getArea()
        {
            return this.Width * this.Height * Math.PI;
        }
        public override string ToString()
        {
            //return base.ToString();
            return getArea().ToString();
        }
    }


    class Square : Shape
    {
        public Square(double w, double h)
        {
            this.Width = w;
            this.Height = h;
        }

        /// <summary>
        /// 计算正方形面积
        /// </summary>
        /// <returns></returns>
        public override double getArea()
        {
            return this.Height * this.Width;
        }
    }
}
多态更多实例请参考http://www.cnblogs.com/jhxk/articles/1644018.html
5.接口
接口相当于没有方法实现的抽象类  ,接口方法不要public
 public interface  IFlyable
 {
        void Fly();
 }
public class Dog:IFiyable 
{ 
  public Dog()
  {} 
  public void Fly()  
  { 
    Consol.write("汪汪");
  }
}

1) 一个类只能继承自一个类,但是可以实现多个接口
2) 一个抽象类中可以定义实现代码,但是接口不能定义实现代码(哪怕方法中只是一个空的括号,也是空实现)。
3) 接口的多态特性、类型转换、is、as 和类基本一样。
抽象类定义的是公共的实现和能力,接口只能定义公共的能力。
接口相当于给类打一个标签,标志这个类有这种能力。
4)   接口中的方法不能声明public、private等,因为没有意义,private是类内部互相调,哪儿来的类内部?
5)   接口中能声明:方法,属性(属性本质是两个方法),索引器,不能声明字段。

常用系统接口
using,实现了IDisposable接口的对象可以使用using进行资源声明,出了using的作用域以后自动调用Dispose方法
Dispose和Close的区别:实现了IDisposable接口必须定义Dispose方法,但不一定有Close方法,很多Dispose的实现都是调用Close方法。SqlConnection Close以后还能重新Open,但是Dispose以后就不能再用。
foreach:实现了IEnumerable接口的对象都可以使用foreach进行遍历
例子
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace Sln0315
{
    class Program
    {
        static void Main(string[] args)
        {
            Bird b1 = new Bird();
            b1.Fly();
            Flyable b2 = b1;
            b2.Fly();
            b1.Walk();

            Runnable r1 = b1;
            r1.Walk();
            //Person p1 = new Chinese();

            IEnumerable c;
            object obj1 = new Bird();
            if (obj1 is Runnable)
            {
                Console.WriteLine("可以飞");
                Runnable r2 = (Runnable)obj1;
                r2.Walk();

                Runnable r3 = obj1 as Runnable;
                r3.Walk();
            }

            using (Bird b5 = new Bird())//实现了IDisposable接口的类可以用using进行资源的管理
            {
            }
            Console.Read();
        }
    }

    public interface Runnable
    {
        void Walk();
    }

    public interface Flyable
    {
       void Fly();
       int Age { get; set; }//因为属性的本质是方法,所以接口中可以定义属性
       //相当于定义了set_Age、get_Age两个抽象的方法
       //void set_Age(int value);
       //int get_Age();
       //int _age;//接口里面不能定义字段
       //private void H1();//为什么不能用private,public,
       //因为如果能private,那么这个方法只有自己能调用,然后自己又实现不了代码,木有意义
    }

    //一个类可以实现多个接口。而一个类只能有一个父类
    //接口中的成员不能有访问级别修饰(public、private等)
    public class Bird : Flyable, Runnable,IDisposable//"实现接口",“继承类”
    {

        public void Fly()
        {
            Console.Write("小鸟飞");
        }

        public void Walk()
        {
            Console.WriteLine("蹦跶的");
        }

        #region Flyable 成员

        private int age;
        public int Age
        {
            get;
            set;
        }

        #endregion

        #region IDisposable 成员

        public void Dispose()
        {
            Console.WriteLine("啊,我死了!!!");
        }

        #endregion
    }
}
6.泛型
列表
1)   ArrayList,可以看做是动态的数组。Add、Clear、Contains、Count、Remove、RemoveAt、ToArray(转换,再没关系)、索引器
2)   C#中所有的数组类型int[]、string[]等都是继承自Array类
3)   集合,ArrayList、HashSet、Hashtable、Dictionary等都可以叫做集合类。实现了IEnumerable(getEnumerator())、IEnumerable<T>的接口都可以使用foreach进行遍历
例子:
有一个字符串是用空格分隔的一系列整数,写一个程序把其中的整数做如下重新排列打印出来:奇数显示在左侧、偶数显示在右侧。比如‘2 7 8 3 22 9’显示成‘7 3 9 2 8 22’
static void Main(string[] args)
        {
            int[] nums = { 3, 8, 9, 33, 32 };
            ArrayList list1 = new ArrayList();
            foreach (int i in nums)
            {
                if (i % 2 == 1)//整除余1就是奇数
                {
                    list1.Add(i);
                }
            }
            foreach (int i in nums)
            {
                if (i % 2 == 0)//整除余1就是奇数
                {
                    list1.Add(i);
                }
            }
            foreach (int i in list1)
            {
                Console.WriteLine(i);
            }
            Console.ReadKey();
        }
泛型
用ArrayList的麻烦的地方:数据放进去就不知道是什么类型的了;不能防止非法类型数据的放入;将ArrayList返回给其他函数,会令调用者很困惑。要区分变量、返回值类型和实际对象类型的区别。IntArrayList,StringArrayList又没完没了
例子:
            ArrayList list1 = new ArrayList();
            list1.Add(3);
            list1.Add(5);
            list1.Add(8);
            list1.Add("12");

            int sum = 0;
            for (int i = 0; i < list1.Count; i++)
            {
                int num = (int)list1[i];
                sum += num;
            }
            Console.WriteLine(sum);
我们需要泛型的ArrayList,所以就有了
List<T>
List<int> 除此之外并无不同,<int>表示List中放的数据类型是int类型的,因为有声明类型时的约定,因此所有方法的参数、返回值都是确定类型的了
List<int> 实现了IEnumerable<int>(泛型版本的IEnumerable)、ICollection<int> (泛型版本的ICollection) 。
所有的List<T>所有的方法也都是泛型的

List<int> list1 = new List<int>();
            list1.Add(3);
            list1.Add(5);
            list1.Add(8);
Dictionary<K,V>
Key-value Pair 键值对。成语(键)不可以重复,但是解释(值)可以重复
Add:添加,如果重复,则报错
索引器方式设置值:可以重复设置,即使不存在也没关系,如果重复则新数据覆盖旧数据
ContainsKey,判断是否存在这个Key
Dictionary<K,V>的非泛型对应的是Hashtable;List<T>→ArrayList

例子:
static void Main(string[] args)
        {
            //<键的类型,值的类型>
            Dictionary<string, int> dict = new Dictionary<string, int>();
            dict.Add("0108888888888", 30);//放进去一个键值对
            dict.Add("1108889998x", 18);
            dict.Add("0108888889999", 22);
            dict.Add("010888888x", 18);//如果添加相同key的项会报错

            //dict.Add("010888888x",19);
            //用索引的方式赋值相当于存在就update,不存在就insert
            //和Add相比,这种方式不会报错
            dict["010888888x"] = 19;
            dict["010888888y"] = 19;

            int age = dict["0108888888888"];//按照key找value
            Console.WriteLine(age);

           // dict.Remove("010888888x");//移除指定key的值
            //dict.Clear();
            //Console.WriteLine(dict.ContainsKey("0108888888888"));
            //Console.WriteLine(dict.ContainsKey("afdasfasdfdsa"));
            //判断是否存在key

            foreach (string key in dict.Keys)
            {
                int value = dict[key];//select * from ...where id=@Id
                Console.WriteLine("{0}={1}", key, value);
            }
            Console.ReadKey();
练习:火星文翻译器
MyDictionary.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace 火星文
{
    class MyDictionary
    {
        private List<MyKeyValue> list = new List<MyKeyValue>();

        public void Add(char key, char value)
        {
            if (ContainsKey(key))
            {
                throw new Exception();
            }
            list.Add(new MyKeyValue() { Key=key,Value=value});
        }

        public bool ContainsKey(char key)
        {
            foreach (MyKeyValue kv in list)
            {
                if (kv.Key == key)
                {
                    return true;
                }
            }
            return false;
        }

        public char this[char key]
        {
            get
            {
                foreach (MyKeyValue kv in list)
                {
                    if (kv.Key == key)
                    {
                        return kv.Value;
                    }
                }
                throw new Exception();
            }
        }
    }

    class MyKeyValue
    {
        public char Key { get; set; }
        public char Value { get; set; }
    }
}
利用仿写的类去实现
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace 火星文
{
    class Program
    {
        private const String Jian = "啊阿埃挨哎唉哀皑癌蔼矮艾碍爱隘鞍氨安俺按暗岸胺案肮昂盎凹敖熬翱袄傲奥懊澳芭捌扒叭吧笆疤巴拔跋靶把";
        private const String HXW = "啊阿埃挨哎唉哀皚癌藹矮艾礙愛隘鞍氨咹俺按暗岸胺案肮昂盎凹敖熬翱襖傲奧懊澳芭捌扒叭吧笆疤巴拔跋靶紦耙";

        static void Main(string[] args)
        {
            MyDictionary dict = new MyDictionary();
            for (int i = 0; i < Jian.Length; i++)
            {
                char chHZ = Jian[i];
                char chHXW = HXW[i];
                dict.Add(chHZ, chHXW);
                //dict[chHZ] = chHXW;
            }

            Stopwatch sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < 1000; i++)
            {
                string s = "我已经不知道现在是白天还是黑天了";
                string sHXW = "";
                foreach (char ch in s)
                {
                    //处理字典中不存在的字符
                    if (dict.ContainsKey(ch))
                    {
                        sHXW = sHXW + dict[ch];//查找汉字对应的火星文
                    }
                    else
                    {
                        sHXW = sHXW + ch;
                    }
                }
            }
            Console.WriteLine(sHXW);
            sw.Stop();
            Console.WriteLine(sw.Elapsed);
            Console.ReadKey();
        }
    }
}
HashSet<T>
不能盛放重复的数据,重复的数据只保留一份
Add(T value)添加元素,Contains(T value)判断是否存在元素
HashSet使用了和Dictionary类似的算法,因此Contains方法效率非常高,时间复杂度为O(1)
面试题:现有1~10共10个整数,已随机放入一个有8个元素的数组a[8]。要求找出没有被放入数组的那2个数
static void Main(string[] args)
        {
            int[] nums = { 9, 8, 5, 3, 1, 10, 2, 7 };
            HashSet<int> set2 = new HashSet<int>();
            foreach (int i in nums)
            {
                set2.Add(i);
            }
            for (int i = 1; i <= 10; i++)
            {
                if (!set2.Contains(i))
                {
                    Console.WriteLine(i);
                }
            }
            Console.ReadKey();
        }
要求找出没有被放入数组的那2个数的更好的解决方案:http://www.rupeng.com/forum/thread-1363-1-1.html
(*)Stack<T>,栈,先入后出,Push(压栈)、Pop(出栈)。
(*)Queue<T>,队列,先入先出:Enqueue(入队)、Dequeue(出队)


注:C#系列总结到这里就结束了,写的有些仓促,有哪些不对的或者缺失的地方还望大家来指正,谢谢

---------------------- ASP.Net+Android+IO开发S.Net培训、期待与您交流! ----------------------
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值