《.NET基础拾遗系列第二篇:.NET的几个基本概念下》
.NET基础拾遗系列第二篇:.NET的几个基本概念(下)
开篇小引
虽然做.NET后台开发很久了,但是我是今年1月底的时候经朋友介绍才发现博客园的,然后每天都像登录腾讯一样,登上博客园,发现这里的人真多,看到了很多高手,像下面提到的和加为关注的,都是我理想要完成的写作,你还不是程序员了,连博客园都没有账户,我来的太晚了!!!看过了anytao的《你必须知道的.net》,看过了伍迷的《大话设计模式》,内心就不安了,之所以觉得他们都是牛人,不错,因为他们都是mvp,都是高手,他们对了.net平台的基础内功都是很深厚,不过鄙人不才,看的书不多,精读的也不多,所以只能写这些基础—《.NET基础拾遗系列》来发闷下,解析内心的夙愿。这些基础概念以及知识点可能就是新手曾经碰到过的,亦或者是投简历面试亲临过的,在这里我将用文字和代码来一起拾遗,希望大家多多给力!如果你是高手不值得看下,或者是久经沙场的大牛,那就请你飘过就可以了,类菌体会记住你的...呵呵…
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
面向对象思想
类和对象的区别
private,protected,internal,public修饰符
属性与get,set
静态的类和成员函数都是静态的
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
面向对象思想
类和对象的区别
private,protected,internal,public修饰符
属性与get,set
静态的类和成员函数都是静态的
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
所以我继续说说一下这几个基本概念,希望说出自己的理解以及值得容易去记住和运用的必要
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
构造函数也有用
重载和重写也参与
接口来了,抽象也来了
最后说说工厂
构造函数也有用
重载和重写也参与
接口来了,抽象也来了
最后说说工厂
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
<六>构造函数也有用
其实看到构造函数,有人可以一句话说出他的作用,即:构造函数用于创建类的实例,并对实例进行初始化操作,通过传递不同的参数,可以进行不同的实例初始化操作,说白了一点就是进行,由于c#的编译器要求严格,所以很强调所以值类型的字段(在c#语言中,数据结构主要分为两大类,一个是值类型,指的是简单类型,整数,布尔...以及结构类型和枚举类型;另一种就是引用类型,包括类类型,数组类型和代理类型以及接口类型)必须在构造函数结束前初始化,当我们在创建对象的此刻,有一个方法就自动执行,他就是构造函数,我说说使用构造函数的一些要注意的地方:
1.当没有构造函数时,c#的编译器会自动提供一个默认的构造函数,即没有任何参数的构造函数,记住,默认的构造函数是公有的
2.当有一个构造函数时,这种情况下,总是调用这一个构造函数,编译器将不会提供默认的构造函数
3.当有多个构造函数时,多个构造函数以不同的签名式区分,使用的时候具体采用方法重载的规则,编译器不提供默认的构造函数
构造函数和类中的方法类似,也是一种函数,不过他的名称必须和类相同,并且构造函数没有返回值,这就是他和一般的函数名称的区别,没有参数的为默认构造函数
代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Gz
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("美国新闻在线开始,下面是最新新闻");
Console.WriteLine(APeople.name + "发表");
News news = new News();
news.ShowNews();
Console.WriteLine("中国新闻联播开始,下面是最新新闻");
Console.WriteLine(HPeople.name + "发表");
news.ShowNews();
Console.ReadLine();
}
}
class News
{
private string NewTitle = "本拉登被枪毙了!";
internal void ShowNews()
{
Console.WriteLine(NewTitle);
}
}
//奥巴马类 静态构造函数,用户初始化静态成员
class APeople
{
internal static string name;
static APeople()
{
name = "奥巴马";
}
}
//胡类 私有构造函数,无法在类外部创建实例
class HPeople
{
internal static string name;
private HPeople()
{
name = "胡";
}
}
}

结果:

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
《七》重载和重写也参与
首先我们来简单对比两者下:
重载
|
重载是方法名称相同,函数签名不同,进行多次重载以适应不同的需要,方法的重载一般通过对类中同名函数的使用不同的签名,此时声明了多个函数体,说简单就是给函数定义不同的参数个数或者不同的参数类型,可以声明不同的同名函数(返回值可以不同)
|
重写
|
重写是进行基类中函数的扩展或者改写,其签名必须与被重写的函数保持一致,派生类中只有继承的的虚方法或者抽象方法可以重写,而静态的方法不能被重写
|
c#允许一个类的多个方法使用相同的名称,这时这些方法参数列表必须各不相同,否则在程序调用的时候就会混淆,编译器区分不同的方法时候并没有考虑到返回值的类型这意味着当两个方法的名称相同,参数也相同的,而只有返回值不同的时候编译就出现报错,但是当同一类型的两个方法名称相同但是参数不同,他们的返回类型可以不同;
代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CzCx
{
class Program
{
static void Main(string[] args)
{
MyPc a = new MyPc();//创建MyPc的实例a
Console.WriteLine("调用无参数的GetInfo()方法");
a.GetInfo();
Console.WriteLine("调用1个参数的GetInfo()方法");
a.GetInfo("金士顿");
Console.WriteLine("调用2个参数的GetInfo()方法");
a.GetInfo("金士顿", 8000);
ShowCpuCompany b = new ShowCpuCompany();//创建ShowCpuCompany的实例b
Console.WriteLine("被重写的 getnameA()方法(抽象)");
b.getnameA();
Console.WriteLine("被重写的 getnameB()方法(虚)");
b.getnameB();
Console.ReadLine();
}
}
class MyPc
{
private string cpu;
private string memory;
private float money;
/// <summary>
/// 默认构造函数
/// </summary>
internal MyPc()
{
cpu = "AMD";
}
/// <summary>
/// 声明无参数的GetInfo()方法
/// </summary>
internal void GetInfo()
{
Console.WriteLine("类菌体的电脑CPU的厂家是{0}",cpu);
}
/// <summary>
/// 声明1个参数的GetInfo()方法
/// </summary>
/// <param name="n"></param>
internal void GetInfo(string n)
{
memory = n;
Console.WriteLine("类菌体的电脑内存的厂家是{0}", memory);
}
/// <summary>
/// 声明2个参数的GetInfo()方法
/// </summary>
/// <param name="m"></param>
/// <param name="p"></param>
internal void GetInfo(string m,int p)
{
this.GetInfo();//调用无参数的GetInfo()方法
this.GetInfo(m);//调用1参数的GetInfo()方法
money = p;
Console.WriteLine("类菌体的电脑的价钱是{0}", money);
}
}
/// <summary>
/// 声明抽象类MyCpu
/// </summary>
abstract class MyCpu
{
/// <summary>
/// 声明抽象方法getnameA()
/// </summary>
abstract internal void getnameA();
/// <summary>
/// 声明虚方法getnameB()
/// </summary>
internal virtual void getnameB()
{
Console.WriteLine("生产CPU的大公司是??");
}
}
/// <summary>
/// 继承MyCpu抽象类
/// </summary>
class ShowCpuCompany : MyCpu
{
/// <summary>
/// 重写抽象方法getnameA()
/// </summary>
internal override void getnameA()
{
Console.WriteLine("生产CPU的大公司是英特尔");
}
/// <summary>
/// 重写虚方法getnameB()
/// </summary>
internal override void getnameB()
{
Console.WriteLine("生产CPU的大公司是AMD");
}
}
}

结果:

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
《八》接口来了,抽象也来了
抽象类只能派生类,而接口可以派生类和结构
抽象类的派生类可以使抽象类,即抽象成员在派生类中不一定被完全实现,而接口要求其派生类或者结构必须完全实现其成员
抽象类可以包含已经实现的成员,可以包含字段,而接口只包含未实现的成员,不能包含字段,并且接口及所含成员必须为public访问级别
类只能继承一个抽象类,但是可以继承(实现)多个接口

先说说接口吧
其实网上有这么说法,接口,就是面向对象设计的重要元素,也是打开设计模式之门的钥匙,玩转接口,你就打开面向对象的抽象之门,成全设计原则,成就设计模式,实现代码艺术,接口在程序设计当中充当类或者是结构的功能界面,接口的属性,方法等属于抽象描述必须通过类或者结构的实现才能使用,我们程序员只知道接口有哪些功能而不知道这些功能如何实现以及由谁实现,这个是现实.
举个例子
某个项目由多个功能模块组成,每一个模块由一个开发者完成,开发者只需要编写完模块功能的实现后,留下该模块的接口工其他人使用,其他人在程序当中只需要使用接口就可以了,二不用了解接口有什么功能,如何实现,看下这个关系模型:

当中的功能模块 没有满足需求或者功能模块的变更时,程序只需要将该功能模块的实现代码部分修改或者扩展,其他的调用模块接口就不用修改也就是所谓的Bridge模式(分离意图和实现)使用接口应注意一下:
接口可以继承多个接口,而类只能继承一个基类(单继承),接口可描述属性,方法,索引器和事件,但是接口只能做声明,无法实现,所有的声明必须由继承此接口的类或者结构来实现,此外接口的访问权限是public,所以类或者结构实现接口的成员必须为public,并且实现的方法的签名必须和接口方法签名一致
接口的使用规则(参考00大智慧)
-
接口隔离原则 强调接口应该被实现为具有单一功能的小接口,而不要实现为具有多个功能的大接口,类对于类的依赖应建立在最小的接口上 -
接口支持多继承,既可以作用于值类型,也可以作用于引用类型
禁止为已发布的接口,添加新的成员,这意味着你必须重新修改所以实现了该接口的类型,在实质应用中,这往往不能实现
- 接口不能被实例化,没有构造函数,接口的成员被隐式声明为public
接口和抽象类的取舍(参考一道面试题答案)
接口和抽象类 非常相似,两者都无法实例化,并且未实现部分都由派生类实现,请看应用模型:

根据图我们对比下接口和抽象类的主要区别:
抽象类只能派生类,而接口可以派生类和结构
抽象类的派生类可以使抽象类,即抽象成员在派生类中不一定被完全实现,而接口要求其派生类或者结构必须完全实现其成员
抽象类可以包含已经实现的成员,可以包含字段,而接口只包含未实现的成员,不能包含字段,并且接口及所含成员必须为public访问级别
类只能继承一个抽象类,但是可以继承(实现)多个接口
在具体的设计当中,抽象类和接口的使用要视程序的需要而定,抽象类可以用于归纳一直相似的共同特性的类,并且把它们的共同成员提取到抽象类来,使得抽象类作为这一组类的基类,作用实现代码复用,节约了代码量,减轻了维护的复杂度,而将这些相似的方法(属性)提取以后成为抽象类中的抽象成员,不再提供具体实现,由这一组类自己完成,(抽象类的应用非常和网页设计的css外部样式文件类似,大量的页面风格相同可以共用这个css文件,并且可以在一起修改css属性),而接口是一组类的功能的集合,即一个经过协定的集合,复杂实现这些功能,接口内含的成员都是抽象的,类可以实现多个接口,这样子将意图和实现
分离,接口可以给其他程序直接使用,并且可以方便地功能扩展....
代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Jk
{
class Program
{
static void Main(string[] args)
{
//创建bacteria的一个对象b
bacteroid b = new bacteroid();
//访问实例b的一个字段MInfo
Console.WriteLine("bacteroid 实例继承的MInfo 是:{0}",b.MInfo);
//调用实例b的getName方法
Console.WriteLine("bacteroid 实例实现getName方法 是:{0}", b.getname());
//访问实例b的ismale属性
Console.WriteLine("bacteroid 实例的ismale属性 是:{0}", b.ismale);
//创建MaKeTuW的一个对象m
MaKeTuW m = new MaKeTuW();
//访问实例m的字段MInfo
Console.WriteLine("MaKeTuW 实例继承的MInfo 是:{0}", m.MInfo);
//调用实例m的getName方法
Console.WriteLine("MaKeTuW 实例实现getName方法 是:{0}", m.getname());
//访问实例m的ismale属性
Console.WriteLine("MaKeTuW 实例的ismale属性 是:{0}", m.ismale);
Console.ReadLine();
}
}
/// <summary>
/// 声明一个抽象类Person
/// </summary>
abstract class Person
{
internal string MInfo = "是一个优秀的大学生";
abstract internal string getname();//声明抽象方法
/// <summary>
/// 声明抽象布尔类型属性ismale
/// </summary>
abstract internal bool ismale
{
get;
}
}
/// <summary>
/// 声明类bacteroid,继承Person
/// </summary>
class bacteroid : Person
{
/// <summary>
/// 实现getName方法
/// </summary>
/// <returns></returns>
internal override string getname()
{
return "我是类菌体";
}
/// <summary>
/// 实现ismale属性
/// </summary>
internal override bool ismale
{
get { return true; }
}
}
/// <summary>
/// 声明类Female,继承Person
/// </summary>
abstract class Female : Person
{
/// <summary>
/// 实现ismale属性
/// </summary>
internal override bool ismale
{
get { return false; }
}
}
/// <summary>
/// 声明类MaKeTuW ,继承Female
/// </summary>
class MaKeTuW : Female
{
/// <summary>
/// 实现getName方法
/// </summary>
/// <returns></returns>
internal override string getname()
{
return "我是MaKeTuW";
}
}
}

结果:

总结:
抽象类中的类体可以包括实现的成员,而未实现的成员是抽象 成员,抽象方法的属性石隐性的virtual,所以派生类实现的抽象方法或者属性必须使用override关键字;继承抽象类的派生类没有完全实现抽象成员,仍然只是抽象类,即派生的非抽象类必须是完全实现抽象成员。抽象类也是实现接口,这时抽象类必须实现所有的接口成员,也可将继承的接口成员映射为抽象成员,并由其派生类来实现,注意抽象类的抽象成员不能使用virtual或者static修饰
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
《九》最后说说工厂
在软件系统中,经常有面对一系列相互依赖的对象要创建,同时由于需求的变化,往往存在着更多的系列的对象要创建工作,其实工厂模式就是为了绕过常规的对象创建的产生的(new创建实例),工厂模式提供了一种“封装机制”来减少使用程序和“多系列具体对象创建的”耦合度“
简单工厂模式可以用于封装创建实例的实现部分,在应用接口的程序中被广泛使用,如图其应用模式:

为了处理更加复杂的情况,我们将图中的产品在细分成多个类,用抽象类进行归纳,完成类中产品共用代码的复用,工厂类也相应地分为多个类,用抽象类进行归纳,如图:

说明,将A产品和B产品作为2类产品,每一个大类产品有2个产品,如A产品有A1和A2..
代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Fo
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("男性中有2中选择,编号如下");
Console.WriteLine("[1]男孩\t[2]男人");
Console.WriteLine("请输入你选择:");
int intput1 = Int32.Parse(Console.ReadLine());//读取用户输入并转化为整型数据
Factory1 f1 = new Factory1();//创建Factory 实例f1
Ihuman h1 = f1.gethuman(intput1);//调用h1的gethuman方法,并传递输入的参数
h1.getfav();//调用h1的getfav方法
Console.WriteLine("我的身份是:{0}",h1.getstatus());
//输出虚线
for(int j = 50; j < 50;j++ )
{
Console.Write("-");
}
Console.WriteLine("女性中有2中选择,编号如下");
Console.WriteLine("[3]女孩\t[4]女人");
Console.WriteLine("请输入你选择:");
int intput2 = Int32.Parse(Console.ReadLine());
Factory2 f2 = new Factory2();
Ihuman h2 = f2.gethuman(intput2);
h2.getfav();
Console.WriteLine("我的身份是:{0}", h2.getstatus());
Console.ReadLine();
}
}
/// <summary>
/// 声明接口Ihuman
/// </summary>
interface Ihuman
{
void getfav();//声明未实现的方法getfav
string getstatus();//声明未实现的getstatus方法
}
/// <summary>
/// 声明抽象类Children,继承实现接口Ihuman
/// </summary>
abstract class Children : Ihuman
{
protected string status = "孩子";//定义字段status并且赋值
public string getstatus()//实现接口 Ihuman的getstatus方法
{
return status; //getstatus方法返回字段status
}
abstract public void getfav();//将接口 Ihuman的getfav方法映射到抽象方法实现
}
/// <summary>
/// 声明一个Boy类,并继承Children
/// </summary>
class Boy : Children
{
/// <summary>
/// 实现重写抽象的getfav方法
/// </summary>
public override void getfav()
{
Console.WriteLine("男孩子喜欢玩手机");
}
}
/// <summary>
/// 声明Girl类,并继承Children
/// </summary>
class Girl : Children
{
/// <summary>
/// 实现重写抽象的getfav方法
/// </summary>
public override void getfav()
{
Console.WriteLine("女孩子喜欢玩小卡片");
}
}
/// <summary>
/// 声明抽象类Adult,并实现接口Ihuman
/// </summary>
abstract class Adult : Ihuman
{
public string status = "成年人";
/// <summary>
/// 实现接口Ihuman的getstatus方法
/// </summary>
/// <returns></returns>
public string getstatus()
{
return status; //getstatus方法返回字段status
}
abstract public void getfav();//将接口 Ihuman的getfav方法映射到抽象方法实现
}
/// <summary>
/// 声明Man类,并继承Adult
/// </summary>
class Man : Adult
{
/// <summary>
/// 实现重写抽象的getfav方法
/// </summary>
public override void getfav()
{
Console.WriteLine("男人喜欢编程");
}
}
/// <summary>
/// 声明Woman类,并继承Adult
/// </summary>
class Woman : Adult
{
/// <summary>
/// 实现重写抽象的getfav方法
/// </summary>
public override void getfav()
{
Console.WriteLine("女人喜欢逛街");
}
}
/// <summary>
/// 声明HumanFactory抽象类
/// </summary>
abstract class HumanFactory
{
/// <summary>
/// 定义Ihuman接口类型的字段,分别创建相应的类实例
/// </summary>
protected Ihuman h1 = new Boy();
protected Ihuman h2 = new Man();
protected Ihuman h3 = new Girl();
protected Ihuman h4 = new Woman();
abstract public Ihuman gethuman(int i);
}
/// <summary>
/// 声明Factory1类,并继承HumanFactory
/// </summary>
class Factory1 : HumanFactory
{
/// <summary>
/// 重写继承的gethuman抽象方法
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
public override Ihuman gethuman(int i)
{
//对参数进行判断,并返回不同的字段值
switch (i)
{
case 1: return h1;
case 2: return h2;
default: return h1;
}
}
}
/// <summary>
/// 声明Factory2类,并继承HumanFactory
/// </summary>
class Factory2 : HumanFactory
{
/// <summary>
/// 重写继承的gethuman抽象方法
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
public override Ihuman gethuman(int i)
{
//对参数进行判断,并返回不同的字段值
switch (i)
{
case 1: return h3;
case 2: return h4;
default: return h3;
}
}
}
}

结果:

说明:在程序当中声明了多个类,代码略显复杂,但是只要根据上面的图就容易看的懂了,在代码当中,首先声明了一个接口Ihuman,其包含两个未实现的方法,其次是接口的类是两个抽象类,即Children和Adult类,这两个类分别派生Boy和Girl以及Man和Woman类,接口的getstatus方法成员在抽象类中实现,而getfav方法则映射为抽象方法,被抽象类的派生类实现,最后通过HunmanFactory抽象类的两个派生类,创建不同的实例引用,返回接口类型
在主程序当中,分别创建Factory1类和Factory2类的实例(f1和f2),并调用其gethuman方法,根据用户的输入决定创建那个类的实例引用,返回一个接口类型的引用变量(h1和h2)
结尾:
谢谢大家给力!!!!
推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架