现在就让我们一起来看一下什么是多态以及如何实现多态。
多态就是不同的对象收到相同的消息时会产生不同的行为。同一个类在不同的场合下表现出不同的行为特征。
多态的作用:把不同的子类对象当做父类来看,可以屏蔽不同的子类对象之间的差异,写出通用的代码,做出通用的编程,增加程序的灵活性和可扩展性,以适应需求的不断变化。
如何实现多态呢?
首先实现多态的条件就是继承(或实现接口)。实现多态的方式可以有以下三种方式:
1、父类成员用virtual关键字修饰,子类可以重写父类成员(此处所指的成员均为子类可以继承的成员)
2、父类成员用abstract关键字修饰,子类可以重写父类的成员而且必须重写(最终)。
3、实现接口,子类可以实现接口中定义的方法而且必须实现。
一、使用virtual实现多态
用virtual修饰的方法称为虚方法。
1、当父类有方法需要让子类重写时,则可以将此方法标记为virtual。
2、虚方法在父类中必须要给出实现,哪怕是空实现。
3、子类可以重写父类的虚方法,也可以不重写。
下面我们就来看一个小例子:
1 static void Main(string[] args) 2 { 3 object str = "你好"; 4 object person = new Person() { Name="张三" }; 5 Console.WriteLine(str.ToString()); //此处输出什么? 6 Console.WriteLine(person.ToString()); //此处输出什么? 7 Console.ReadKey(); 8 } 9 10 public class Person 11 { 12 public string Name 13 { 14 get; 15 set; 16 } 17 }
聪明的你是不是已经知道了输出的结果呢?没错,第一行输出“你好”,第二行输出“命名空间.Person”,其中的命名空间据你的项目而定,同样都是object对象,为什么会出现这种情况呢?要解决这个问题,我们先得了解一下ToString()方法。
ToString()是object类定义的一个虚方法,其实现的功能为把当前对象的类型装换为字符串输出。那么第一行为什么没有输出对象的类型呢?以为string类重写了ToString()方法,把当前对象输出了(即把string对象本身输出)。由于Person类没有重写ToString()方法,所以输出的是当前对象的类型。
下面我们把Person类的代码稍稍改动一下。
public class Person { public string Name { get; set; } public override string ToString() { return this.Name; } }
此时第二行就会输出“张三”。
二、使用abstract实现多态
先来看一下有关抽象类的相关概念:
1、abstract关键字不光可以修饰类成员,还可以修饰类。被abstract修饰的类称为抽象类,被abstract修饰的方法称为抽象方法。
2、抽象类不能被实例化,如果要想实例化,则必须有子类继承他,并且抽象类变量只能指向实现了他中的所有抽象成员的子类对象。
3、抽象类的子类如果没有实现他的抽象方法,或者只是实现了一部分,则这个类也必须是抽象类。
4、如果一个类中包含抽象方法,则这个类必须声明为抽象类。反之,抽象类中不一顶含有抽象方法。
5、抽象方法必须不能给出任何实现。
6、抽象类既可以包含抽象成员,又可以包含具体代码成员。
7、抽象类不能用static或sealed修饰。抽象方法不能用static或private修饰。
8、抽象类中有构造函数,而且可以重载。
说了好多,我都有点凌乱了,哈哈……
记得曾经有人和我争论一个问题,说抽象类可以不被子类继承(很纠结的问题),这句话百分之百是对的,但是我们在项目中定义一个抽象类,而不用子类去继承他,那么它的存在还有什么意义呢?既然我们定义了抽象类,说明我们一定要用他,既然要用它就必须要有子类继承他(因为他本身不能被实例化)。(个人观点,有些偏激)
下面我们来做一个抽象类的小例子:
“橡皮鸭子(RubberDuck)、真实的鸭子(RealDuck)。两个鸭子都会游泳,而橡皮鸭子和真实的鸭子都会叫,只是叫声不一样,橡皮鸭子“唧唧”叫,真实地鸭子“嘎嘎”叫”
public abstract class Duck { public string Name { get; set; } public void Swim() { Console.WriteLine("我是{0},I can swimming!",this.Name); } public abstract void Speek(); } public class RubberDuck:Duck { public RubberDuck(string name) { this.Name = name; } public override void Speek() { Console.WriteLine("唧唧..."); } } public class RealDuck : Duck { public RealDuck(string name) { this.Name = name; } public override void Speek() { Console.WriteLine("嘎嘎..."); } } static void Main(string[] args) { Duck duck1 = new RubberDuck("RubberDuck"); Duck duck2 = new RealDuck("RealDuck"); duck1.Swim(); duck1.Speek(); duck2.Swim(); duck2.Speek(); Console.ReadKey(); }
至于输出结果,大家在脑子中输出一下吧。。。
三、使用接口实现多态
神马是接口?
接口就是一种规范,一种协议,约定好遵守某种协议就可以写出通用的代码。
1、接口中定义了若干个具有各自功能的方法,只是表示一种能力,并没有具体实现。
2、接口中的成员不能有访问修饰符,默认public。
3、接口中可以有属性,方法,索引器,事件等(归根结底都是方法),但是不能有字段(可以有静态)。
4、实现接口的子类必须实现该接口的全部成员。(直接子类)
5、一个类可以同时实现多个接口,接口也可以继承接口。
6、接口不能被实例化。
7、如果继承接口的类不想全部实现接口中的成员,则可以把这个类声明为抽象类,把不实现的方法声明为抽象方法。
8、如果一个类同时继承了类和实现了接口,则必须把类放在前面。
是不是又凌乱了呢?好吧,先说这么多吧。
有时候我们总是觉得既然有了抽象类,还要接口做什么呢?其实接口是有他的存在意义的。比如我们看下面的例子:
“一架直升飞机和一只麻雀都会飞,但是一架直升飞机属于飞机类,一只麻雀属于鸟类,飞机不具备鸟类的共有属性,同理,鸟类也不具有飞机类的共有特性,因此他们不能抽象一个共有的父类(否则从他们的父类派生的子类就会同时具备飞机和鸟类的全部特性,这会造成子类冗余)。那么在这种情况下,我们要想对”飞“这个能力实现多态,该怎么办呢?对,用接口。
public class Plane { public string Name { set; get; } public void LoadPeople() { Console.WriteLine("我可以载人"); } } public class Bird { public string Name { set; get; } public void Raise() { Console.WriteLine("我可以繁殖后代"); } } public interface IFlyable { void Fly(); } public class VertiPlane:Plane,IFlyable { public void Fly() { Console.WriteLine("我是{0},I can Flying!",this.Name); } } public class Sparrow : Plane, IFlyable { public void Fly() { Console.WriteLine("我是{0},I can Flying!", this.Name); } } static void Main(string[] args) { IFlyable plane = new VertiPlane() { Name="直升飞机"}; IFlyable bird = new Sparrow { Name = "麻雀" }; plane.Fly(); bird.Fly(); Console.ReadKey(); }
此时如果我们要用这两个对象的其他属性怎么办呢?可以显示转换为想要的类型(前提是他指向的对象必须是将要转换的类型)
从本文标题中可以看出,主要说的是反射技术和控制反转(IOC)技术,本文主要先介绍一下我对这两种技术的理解及它们的优缺点,最后再用实例来说一下使用方法。
反射:可以使用反射动态创建类型的实例,将类型绑定到现有对象,或从现有对象获取类型并调用其方法或访问其字段和属性。这里,它最重要的是“动态性”,即根据条件动态创建“指定类型”的“实例”。
1 // Using GetType to obtain type information: 2 int i = 42; 3 System.Type type = i.GetType(); 4 System.Console.WriteLine(type);
结果是:
System.Int32
本示例使用静态方法 GetType(Object 基类派生的所有类型都继承该方法) 获取变量类型的简单反射实例
1 // Using Reflection to get information from an Assembly: 2 System.Reflection.Assembly o = System.Reflection.Assembly.Load("mscorlib.dll"); 3 System.Console.WriteLine(o.GetName());
结果是:
mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
本示例使用反射获取已加载的程序集的完整名称
反射一般用在以下情况中:
-
需要访问程序元数据的属性。linq to sql 中使用很多
-
执行后期绑定,访问在运行时创建的类型的方法。与工厂模式一起使用,根据配置文件中的类型来动态建立实例
IOC:(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题。 控制反转还有一个名字叫做依赖注入(Dependency Injection)。简称DI。实现IOC的架构有很多如:Avalon 、Spring、JBoss及Unity等。
理解IOC:可以把IoC模式看做是工厂模式的升华,可以把IoC看作是一个大工厂,只不过这个大工厂里要生成的对象都是在XML文件中给出定义的,然后利用Java 的“反射”编程,根据XML中给出的类名生成相应的对象。
实现非常简单,根据容易名称去创建对象即可
1 /// <summary> 2 /// The static factory of container 3 /// </summary> 4 public sealed class ContainerManager 5 { 6 /// <summary> 7 /// Creates the specified container instance . 8 /// </summary> 9 /// <param name="containerName">Name of the container.</param> 10 /// <returns></returns> 11 public static IContainerContext GetContainer(string containerName) 12 { 13 return new UnityContainerContext(containerName); 14 } 15 }
以下是在实际项目中的使用,IOC架构是用Unity,它的基础代码是:
1 /// <summary> 2 /// The specific container context for Unity 3 /// </summary> 4 public class UnityContainerContext : ContainerContextBase 5 { 6 #region Fields 7 8 /// <summary> 9 /// The lock used for synchronous 10 /// </summary> 11 private static readonly object _synlock = new object(); 12 13 #endregion 14 15 #region Constructor 16 17 /// <summary> 18 /// Initializes a new instance of the <see cref="UnityContainerContext"/> class. 19 /// </summary> 20 /// <param name="name">The name.</param> 21 public UnityContainerContext(string name) 22 : base(name) 23 { 24 } 25 26 #endregion 27 28 #region Properties 29 30 /// <summary> 31 /// Gets the current context. 32 /// </summary> 33 /// <value>The current context.</value> 34 private HttpContext CurrentContext 35 { 36 get 37 { 38 HttpContext context = HttpContext.Current; 39 if (context == null) 40 { 41 throw new Exception("The current httpcontext is null"); 42 } 43 return context; 44 } 45 } 46 47 #endregion 48 49 #region Override Methods 50 51 /// <summary> 52 /// Initializes container. 53 /// </summary> 54 public override void Initialize() 55 { 56 OnBeforeInitialized(new ContainerEventArgs(this, ContainerType.Unity)); 57 58 if (CurrentContext.Application[Name] == null) 59 { 60 lock (_synlock) 61 { 62 if (CurrentContext.Application[Name] == null) 63 { 64 IUnityContainer currentContainer = new UnityContainer(); 65 UnityConfigurationSection section = ConfigurationManager.GetSection("unity") as UnityConfigurationSection; 66 section.Containers[Name].Configure(currentContainer); 67 CurrentContext.Application[Name] = currentContainer; 68 } 69 } 70 } 71 72 OnAfterInitialized(new ContainerEventArgs(this, ContainerType.Unity)); 73 } 74 75 /// <summary> 76 /// Resolves this instance. 77 /// </summary> 78 /// <typeparam name="T">Parameter type.</typeparam> 79 /// <returns></returns> 80 public override T Resolve<T>() 81 { 82 try 83 { 84 Initialize(); 85 86 IUnityContainer currentContainer = CurrentContext.Application[Name] as IUnityContainer; 87 return currentContainer.Resolve<T>(); 88 } 89 catch(Exception ex) 90 { 91 OnResolveFailed(new ContainerFailedEventArgs(this, ContainerType.Unity, ex)); 92 return default(T); 93 } 94 } 95 96 /// <summary> 97 /// Tears down. 98 /// </summary> 99 public override void TearDown() 100 { 101 OnBeforeTearDown(new ContainerEventArgs(this, ContainerType.Unity)); 102 103 CurrentContext.Application[Name] = null; 104 105 OnAfterTearDown(new ContainerEventArgs(this, ContainerType.Unity)); 106 } 107 108 #endregion 109 110 }
在项目中通过unity来创建对象的代码是:
1 /// <summary> 2 /// 数据层实体的个性操作对象 3 /// </summary> 4 /// <typeparam name="TEntity"></typeparam> 5 /// <returns></returns> 6 protected TEntity LoadRepositoryEntity<TEntity>() 7 { 8 IContainerContext container = ContainerManager.GetContainer("repositoryContainer"); 9 return container.Resolve<TEntity>(); 10 }
这样,在BLL层调用DAL层对象时,可以通过LoadRepositoryEntity泛型方法来实现。