探讨继承与实现(一)

PS:原文名《探讨C#的继承》 。经双鱼座的提醒,改名为探讨继承与实现。并把文章关于接口继承的描述改为实现。顺便把部分内容进行了改写,以使行文更加流畅,内容更加丰满。修改部分标识为蓝色。 2007年2月1日

2006年6月19日         By   谢平
http://www.cnblogs.com/birdshover/

继承是建立一个类,然后创建它的更特殊的版本。是OOP中不可缺少的部分。
人在描述事物的时候是有层次的,那么继承就使程序对现实世界有层次的描述的表达成为可能。对程序员来说继承的重点是共同点。因为有共同点才能重用。

实现是建立一个接口,然后由某些类来实现接口描述的细节。就好比是工程师绘制了部件,然后由工人做出具体的产品,工程师并没有去制造部件。

类与接口的区别就在于此,类不但有描绘,还实现了部分细节。而特殊的抽象类完全带抽象方法,就和接口完成的同样功用。而且,无论接口还是类,都不是凭空去描绘出来的 ,否则就是纸上谈兵了,而是根据具体类的特征抽象出来的。所以他们有共同点。


目录
一、继承与实现
      接口实现示例
      类继承示例
二、继承,实现简单论述
      1、接口的实现以及接口对类的访问
      2、类的继承关于以及父类对子类的访问

一、继承与实现
         抽象有两种抽象方式。
         1、他们有相同的工作方式;
         2、他们有相同的名称、作用和特征。
          第一种方式是类的继承,而第二种方式是接口的实现。

         比如:数据库操作中一般都会用到 添加,删除,更新,读取。那么就是他们都有相同的名称、作用和特征。就可以使用接口实现。

using System.Collections;
using System.Data;

interface IMyData
{
    
void MyInsert(ArrayList al);
    
void MyDelete(ArrayList al);
    
void MyUpdate(ArrayList al);
    DataSet MySelect(ArrayList al);
}

using System;

namespace FmRadio
{
    
/// <summary>
    
/// DMusicSort 的摘要说明。
    
/// </summary>

    public class DMusicSort : IMyData
    
{
        
public DMusicSort()
        
{
            
//
            
// TODO: 在此处添加构造函数逻辑
            
//
        }

        
IMyData 成员
    }

}

再比如:数据库的连接,这里只说MS Sql Srever 2000,在一个程序只使用一个库的情况下,所有关于数据库操作类都将执行相同的数据库连接。他们有相同的工作方式。

using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;

namespace FmRadio
{
    
/// <summary>
    
/// MyConnection 的摘要说明。
    
/// </summary>

    public class MyConnection
    
{
        
private SqlConnection pConn;

        
public MyConnection()
        
{
            
//
            
// TODO: 在此处添加构造函数逻辑
            
//
            pConn = new SqlConnection(ConfigurationSettings.AppSettings["DbLink"]);
        }


        
public SqlConnection conn
        
{
            
get
            
{
                
// TODO:  添加 MyConnection.conn getter 实现
                return pConn;
            }

        }

    }

}

using System;
using System.Data;
using System.Data.SqlClient;
using System.Collections;

namespace FmRadio
{
    
/// <summary>
    
/// Execute 的摘要说明。
    
/// </summary>

    public class Execute : MyConnection
    
{
        
private int sType = 0;
        
private ArrayList pPara;
        
private string pContext;

        
public Execute()
        
{
            
//
            
// TODO: 在此处添加构造函数逻辑
            
//
        }


        
public Execute(string pContext,ArrayList pPara)
        
{
            
this.pPara = pPara;
            
this.pContext = pContext;
        }


        
public Execute(int pType,string pContext,ArrayList pPara)
        
{
            
this.sType = pType;
            
this.pPara = pPara;
            
this.pContext = pContext;
        }


        
public int ExeNone()
        
{
            
// TODO:  添加 Execute.ExeNone 实现
            SqlCommand sc = null;
            CommandType ct 
= CommandType.StoredProcedure;
            
if(this.sType==0)
                ct 
= CommandType.StoredProcedure;
            
else
                ct 
= CommandType.Text;

            
int rs = 0;
            sc 
= new SqlCommand();
            sc.CommandType 
= ct;
            sc.Connection 
= conn;
            sc.CommandText 
= pContext;
            
foreach(DataPara dp in pPara)
            
{
                sc.Parameters.Add(
"@" + dp.ColName,dp.ColContent.GetType()).Value = dp.ColContent;
            }

//            try
//            {
                conn.Open();
                rs 
= sc.ExecuteNonQuery();
                conn.Close();
//            }
//            catch{}
            return rs;
        }


        
public DataSet ExeTable()
        
{
            
// TODO:  添加 Execute.ExeTable 实现
            SqlDataAdapter sda = new SqlDataAdapter();
            SqlCommand sc 
= null;
            CommandType ct 
= CommandType.StoredProcedure;
            
if(this.sType==0)
                ct 
= CommandType.StoredProcedure;
            
else
                ct 
= CommandType.Text;

            sc 
= new SqlCommand();
            sc.CommandType 
= ct;
            sc.Connection 
= conn;
            sc.CommandText 
= pContext;
            
foreach(DataPara dp in pPara)
            
{
                sc.Parameters.Add(
"@" + dp.ColName,dp.ColContent.GetType()).Value = dp.ColContent;
            }

            sda.SelectCommand 
= sc;
            DataSet ds 
= new DataSet();
//            try
//            {
                conn.Open();
                sda.Fill(ds,
"asdasd");
                conn.Close();
//            }
//            catch{}
            return ds;
        }


    }

}


二、继承,实现简单论述
下面,我们找更加简单的例子来探讨继承和实现。

1、接口的实现以及接口对类的访问
interface A
{
    
void a();
}

using System;

namespace Mocl
{
    
/// <summary>
    
/// A的摘要说明。
    
/// </summary>

    public class A : IA    
    
{
        
public A()
        
{
            
//
            
// TODO: 在此处添加构造函数逻辑
            
//
        }

        
IA 成员

        
public void b()
        
{
            System.Diagnostics.Debug.WriteLine(
"b");
        }

    }

}

执行以下方法
using System;

namespace Mocl
{
    
/// <summary>
    
/// Class1 的摘要说明。
    
/// </summary>

    class Class1
    
{
        
/// <summary>
        
/// 应用程序的主入口点。
        
/// </summary>

        [STAThread]
        
static void Main(string[] args)
        
{
            
//
            
// TODO: 在此处添加代码以启动应用程序

            A a 
= new A();
            a.a();
            a.b();
            IA c 
= new A();
            c.a();
        }

    }

}


输出
a
b
a

IA c = new A();通过接口访问类。注意使用IA c = new A();的时候,对象c是访问不到类A的方法成员void b()的。因为父类访问子类的时候只会返回父类所定义的方法成员的引用。

我们可以再给类A 添加个字段 public int i = 0;
A的方法a换成 
  public void a()
  {
   // TODO:  添加 A.a 实现
   System.Diagnostics.Debug.WriteLine("a"  + i.ToString());
  }

继续执行,发现输出
a0
b
a0
从这点上看出接口对子类的引用并不阻止类执行在访问过程中要使用到的成员,无论这些成员是否是接口的一部分。

2、类的继承关于以及父类对子类的访问
我是在网上看了类似的问题,才有兴趣对继承的各种关系进行学习。才有此文,和大家分享我的学习体会。所以这里就用他的原问题,进行探讨。

public class A
{
 
public  void printA()
 
{
  System.Diagnostics.Debug.WriteLine(
"printA");
 }

 
public virtual void valueA()
 
{
  System.Diagnostics.Debug.WriteLine(
"ValueA");
 }

}


public class B:A
{
 
new public void printA()
 
{
  System.Diagnostics.Debug.WriteLine(
"printB");
 }


 
public override void valueA()
 
{
  System.Diagnostics.Debug.WriteLine(
"ValueB()");
 }

}


using System;

namespace Mocl
{
    
/// <summary>
    
/// Class1 的摘要说明。
    
/// </summary>

    class Class1
    
{
        
/// <summary>
        
/// 应用程序的主入口点。
        
/// </summary>

        [STAThread]
        
static void Main(string[] args)
        
{
            
//
            
// TODO: 在此处添加代码以启动应用程序
            
//
            B BTemp=new B(); 
            A ATemp
=BTemp; 
            ATemp.printA(); 
            ATemp.valueA(); 
            BTemp.printA(); 
            BTemp.valueA();
        }

    }

}


输出:
printA
ValueB()
printB
ValueB()

直接对B的访问B BTemp=new B(); 肯定都能理解。但是A对B的访问 A ATemp=BTemp; (与 A ATemp= new B(); 相当)为什么会得到这样的输出呢?

在讨论问题之前,先来看两个关键字virtual 和new
1、new 的意义
         new是在程序编译过程中指定方法引用新的指向地址。
2、virtual 的意义
         virtual是在程序运行时确定方法引用的地址。


问题1:类B的方法
 new public void printA()
 {
  System.Diagnostics.Debug.WriteLine("printB");
 }
不是覆盖了类A的
 public  void printA()
 {
  System.Diagnostics.Debug.WriteLine("printA");
 }
了吗?怎么还会输出printA呢?

那是因为new方法只在本类被实例化时才会覆盖父类的方法,它并不直接覆盖父类的方法。
而A ATemp= new B(); 由于类A在编译的时候已经确定了它的结构,除了标明virtual 的成员都已经被固定,而virtual 成员的引用地址是动态执行的。程序运行时,在父类访问子类的时候,需要把子类转换成父类,而子类对virtual 的覆盖将告诉父类的virtual 成员引用的实际地址。new 关键字是不起作用的。new是在编译的时候确定的新引用地址,不会对父类产生影响。

问题2:类B的 
public override void valueA()
 {
  System.Diagnostics.Debug.WriteLine("ValueB()");
 }
方法为什么覆盖了父类的
 public virtual void valueA()
 {
  System.Diagnostics.Debug.WriteLine("ValueA");
 }
方法。

答案就是上面的
程序运行时,在父类访问子类的时候,需要把子类转换成父类,而子类对virtual 的覆盖将告诉父类的virtual 成员引用的实际地址。


好了,该上班了,先讨论到这。等有时间的时候再写 《探讨C#的继承(二)》 深入得讨论一下吧。
posted @ 2006-06-19 14:55  Birdshover  阅读(4408)  评论(5编辑  收藏  举报