面向对象(续篇)
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 interface IFlyable
{
void Fly();
}
public class Dog:IFiyable
{
public Dog()
{}
public void Fly()
{
Consol.write("汪汪");
}
}
2) 一个抽象类中可以定义实现代码,但是接口不能定义实现代码(哪怕方法中只是一个空的括号,也是空实现)。
3) 接口的多态特性、类型转换、is、as 和类基本一样。
抽象类定义的是公共的实现和能力,接口只能定义公共的能力。
接口相当于给类打一个标签,标志这个类有这种能力。
5) 接口中能声明:方法,属性(属性本质是两个方法),索引器,不能声明字段。
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.泛型
2) C#中所有的数组类型int[]、string[]等都是继承自Array类
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<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>
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();
练习:火星文翻译器
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>
面试题:现有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(出队)