要进入debug文件,除了右键进入资源管理器之外,也可以在解决方案资源管理器右上方,点击显示全部文件按钮,会列出当前项目的文件列表,把要放的文件放入debug即可;
多态的好处 1,减少代码量 2.屏蔽各个子类之间的差异,写出通用的代码;
C#中的访问修饰符;
public 公开的,公共的
private 私有的,只能在当前类的内部访问
protect 受保护的, 只能在当前类的内部以及该类的子类中访问,
internal 只能在当前项目中访问,在同一项目中,internal和public的访问修饰符是一样的,
protected inrernal 它的权限是protected权限加上internal的权限;
修饰类的访问修饰符只有两个,public internal,如果不写类的访问修饰符,默认是internal,表示只有在当前项目中才能访问这个类,出了这个项目就访问不到了,因此我们为了 让在别的项目里也能访问到这个类的话,我们在建类的时候在类的前面加上public
internal的权限和protected相比,在同一个项目中。Internal的权限要比protected大,但是一旦垮了项目,protected的权限要比internal的权限大,因为不同项目之间依然存在继承关系, internal访问不到了,但是由于继承关系,protected成员依然可以跨项目访问,(跨项目的时候1要添加引用,2要添加命名空间);
在命名空间中定义的元素无法显示的声明为private,protected,protected internal;
类中的成员五个访问修饰符都可以定义;
可访问性不一致,子类的访问权限不能高于父类的访问权限;原因是会暴漏父类的成员;
简单工厂设计模式 设计这个项目的一种方式; 有一个牛人把我们写程序当中经常会遇到的问题,没一个问题都写成了一个解决方案;这一个解决方案就是一个设计模式,有23个设计模式;如果把23种设计模式从头到尾敲一遍就是大牛了, 设计模式帮助我们解决在日常开发中遇到的问题,
简单工厂设计模式最核心的就是工厂,这个工厂最后根据用户的输入来返回一个(笔记本对象),但是用户输入的东西是不知道的,只能给他返回一个父类,但是父类里面装的是子类的对象,
重写父类的方法的时候,子类的方法名字和父类被重写方法的名字是一样的,
public abstract class NoteBook
{
public abstract void SayHello();
}
public class Lenovo : NoteBook
{
public override void SayHello()
{
Console.WriteLine("我是联想");
}
}
public class IBM : NoteBook
{
public override void SayHello()
{
Console.WriteLine("我是IBM");
}
}
public class Acer : NoteBook
{
public override void SayHello()
{
Console.WriteLine("我是宏基");
}
}
public class Dell : NoteBook
{
public override void SayHello()
{
Console.WriteLine("我是戴尔");
}
}
public class Test
{
public static NoteBook GetNoteBook(string brand) //简单工厂的核心,根据用户的输入创建对象赋值给父类;其实还有一个核心的还是用抽象类实现了多态;
{
NoteBook nb = null;
switch(brand)
{
case"Lenovo":
nb = new Lenovo();
break;
case"IBM":
nb = new IBM();
break;
case"Acer":
nb = new Acer();
break;
case"Dell":
nb = new Dell();
break;
}
return nb; //简单工厂最核心的代码部分;
}
}
string brand = Console.ReadLine();
NoteBook nb = GetNoteBook(brand);
nb.SayHello();
值类型:int double char decimal bool enum struct
引用类型:string 数组 自定义类 集合 object 接口
值类型在复制的时候,传递的是这个值的本身,
person p1 = new person();
p1.Name = "张三";
person p2 = p1;
//这行代码表示p1,p2指向同一个对象(p1的new person());现在只要改变p1或者p2的其中任何一个,(p1的new person())其中另外一个值就会变;
//简单的说,p1和p2指向同一块内存,这个时候只要改变p1,那么p2也跟着改变,只改变p2,那么p1也跟着改变; p2.Name = "李四"; //把p2变成了李四,那么p1也成了李四; Console.WriteLine(p1.Name); 输出的是"李四";
引用类型在复制的时候,传递的是对这个对象的引用(地址);
person p = new person();
p.Name = "张三";
Test(p);
Console.WriteLine(p.Name); //p.Name 为李四;
public static void Test(person pp)
{
person p = pp;
p.Name = "李四";
}
不管是形参还是实参都是实实在在在内存中占空间的,
string s1 = "张三";
string s2 = s1;
s2 = "李四";
Console.WriteLine(s1); //张三
虽然是引用类型,但是字符串的不可变性;
Console.WriteLine(s2); //李四
int number = 10;
TestTwo(number);
Console.WriteLine(number); //输出10;
public static void TestTwo(int n)
{
n+=10;
}
int number = 10;
TestTwo( ref number);
Console.WriteLine(number); //输出20;
public static void TestTwo( ref int n)
{
n+=10;
}
ref能够把一个变量,以参数的形式带到一个方法中进行改变,再将改变完成后的值带出方法,
ref的原理是把值类型的值传递变为引用传递,使得两个值类型共占一块内存空间(地址);
要看变量在栈上的地址,设个断点,运行,然后在即时窗口输入&变量名,按回车;
序列化:就是将对象转换为二进制;
反序列化:就是将二进制转换为对象,
序列化和反序列化的作用:传输数据;
在网络中不管传输什么内容,先把传输的内容二进制,对方接收到的也是二进制,它需要干另外一件事就是反序列化;把这个二进制 反序列化为对象;
public class Person
{
private string _name;
public string Name
{
get{return _name;}
set{_name = value}
}
private int _age;
public int Age;
{
get{return _age;}
set{_age = value}
}
private char _gender;
public char Gender
{
get{return _gender;}
set{_gender = value}
}
}
序列化的第一步 1.将这个类标记为可以被序列化的,在一个类的上面写上[Serializable]指示一个类可以被序列化,此类不可以被继承;
只有被[Serializable]标记的类创建出来的对象 才能被序列化;
person p = new person();
p.Name = "张三";
p.Age = 19;
p.Gender = ‘男‘;
using(FileStream fsWrite = new FileStream(@"C:\Users\hhs\Desktop\1.txt",FileMode.OpenOrCreate,FileAccess.Write))
{ //开始序列化对象
BinaryFormatter bf = new BinaryFormatter();
bf.Serializa(fsWrite,p);
}
Console.writeline("序列化成功");
//反序列化
Person p;
using(FileStream fsRead = new FileStream(@"C:\Users\hhs\Desktop\1.txt",FileMode.OpenOrCreate,FileAccess.Read))
{
BinaryFormatter bf = new BinaryFormatter();
p = (Person)bf.Deserializa(fsRead);
}
Console.WriteLine(p.Name);
Console.WriteLine(p.Age);
Console.WriteLine(p.Gender);
学一个东西一定要知道它是干嘛用的;
在一个命名空间下不允许定义一个重名的类,也不允许在一个类中定义重名的方法(前提是它们没有重载)
我们有的时候必须要在一个命名空间下定义重名的类,比如三个人共同做一个项目,这时候都要写person类,
public partial class Person
{
}
public partial class Person
{
}
//这两个都是person类的一部分,共同组成了person类,这样的话就可以同时对person类进行编程,而且一个部分类的成员在另一个部分类里面全都可以访问; 但是部分类里面不允许有重名的方法(不包括重载)
public sealed class Person //密封类最大的特点就是不能被继承;但是能继承别的类;
{
}
重写toString()方法; 如果直接对象.ToString(),那么打印出来的是它的命名空间;
所有的类都能ToString(),因为所有类都直接或间接继承object类,ToString()是object类的方法;
因为子类不能调用父类的抽象方法,但是子类可以调用父类的虚方法,所以ToString()是虚方法,因为我们可以重写它,子类可以重写父类的虚方法;(当然子类也可以重写父类的抽象方法)
public class Person
{
public override string Tostring()
{
return "Hello World";
}
}
重写了Tostring()方法,但是只是Person类的ToString();其他类的ToString()依旧没变;
接口:
public class Person
{
public void CHLSS()
{
Console.WriteLine("我是人类,我可以吃喝");
}
}
//public class NBAPlayer
//{
// public void KouLan()
// {
// Console.WriteLine("我可以扣篮");
// }
}
public class Student : Person,IKouLanable
{
public void KouLan()
{
Console.WriteLine("我也可以扣篮");
}
}
//由于Student继承了Person类,因此他可以吃喝,但是同时他也想实现可以扣篮,由于继承具有单根性,不能既继承Person,又继承NBAPlayer,这时候我们可以考虑将NBAPlayer定义成一个接口
public interface IKouLanable
//以I开头以able结尾表示一种能力
{
void KouLan();
}
定义成接口之后,Student就可以既继承Person,又可以继承NBAPlayer接口了,
一个类继承了接口,必须要实现这个接口的成员,快速实现Alt+shift+F10
什么时候需要用到接口, 1.类需要实现多继承;因为类是不允许多继承的;
接口就是一个规范,能力;
接口:
[public] interface I...able
{
成员;
}
public interface IFlyable
{
void Fly(); //比抽象方法更省事;
接口中的成员不允许添加访问修饰符;默认就是public,接口中没有方法体,类中成员不添加默认就是private
//抽象类中普通的方法可以有方法体,但是抽象函数不可以,但是在接口中不允许写有方法体的成员;
//接口中不能包含字段;接口不是用来存储数据的,存储数据用类来存,
string Test();
string Name
{
get; //自动属性; 接口中能写方法,但是不能有方法体,属性的本质是get,set方法,因为自动属性没有get,set方法体,因此,接口中可以有自动属性,
set; //get和set本质上也是函数,因此这个自动属性和上面的fly没有区别;
}
}
private string _name;
public string Name
{
get{return _name;}
set{_name = value;}
}
public int Age
{
get;
set;
}
自动属性,虽然我们没有给它写字段,但是在编译的时候会自动的给我们生成一个字段; 自动属性和普通属性的区别,一个有字段,有方法体 ;一个没有字段,没有方法体; 自动属性的缺点,不能在属性里面进行值的限定了,不允许写方法体,那么要对值进行限定就要使用构造函数了;各有各的好处; 自动属性本质上是两个函数,
接口中可以有方法和属性,还可以有索引器(索引器还没学过;)这三个东西本质上都是方法;接口中只能有方法;
接口是一种规范,只要一个类继承了一个接口,这个类就必须实现这个接口中所有的成员;
一个类中,去实现接口中的方法,没有override,有override是重写;
为了多态,接口不能被实例化,也就是说,接口不能new(不能创建对象);
抽象类,静态类,接口不能被实例化;抽象类,接口由于没有对方法进行实现,因此即使创建了对象也没有意义; 因此要实现多态的话,要去指向一个子类对象;
//实现多态的时候,3种方法一般都是父类对象装子类;三种方式都是在继承的基础上实现多态;
IFlyable fly = new Person();
fly.Fly();
public class Person:IFlyable
{
public void Fly()
{
Console.WriteLine("人类在飞");
}
}
public class Bird:IFlyable
{
public void Fly()
{
Console.WriteLine("鸟类在飞");
}
}
public interface IFlyable
{
void Fly();
}
接口中的成员不能加“访问修饰符”,接口中的成员访问修饰符为public,不能修改;
接口中的成员不能有任何实现,这个很我们的抽象类当中的抽象函数是一样的,(都是光说不做,交给子类去做,接口只是定义了一组未实现的成员);
接口中只能有方法,属性,索引器,事件,这四个本质上都是方法,不能有“字段”和构造函数;不能有字段是因为接口不是用来存储数据的,它表示一种规范,一种能力;
因为接口不能创建对象,因此即使有构造函数没有用
接口与接口之间可以继承,并且可以多继承;类就不能多继承;
public interface M1
{
void Test1();
}
public interface M2
{
void Test2();
}
public interface M3
{
void Test3();
}
public interface SuperInterface:M1,M2,M3
{
}
public class Car : SuperInterface //继承一个大接口,他所继承的接口都要实现;
{
public void Test1();
{
}
public void Test2();
{
}
public void Test3();
{
}
}
接口并不能去继承一个类,而类可以继承接口(接口只能继承接口。而类既可以继承接口,也可以继承类)
实现接口的子类必须实现该接口的全部成员;
一个类可以同时继承一个类并实现多个接口,如果一个子类同时继承了父类A,并实现了接口IA,那么语法上A必须写在IA的前面; class MyClass:A,IA{},因为类是单继承的.
说是说面向对象的编程,但是很多情况下其实就是面向接口的编程;
比如说手机中的天气预报,有很多个APP都有天气;但是这些数据源头是中央气象台; 中央气象台对外提供了一个接口,那么我只要实现这个接口中的成员,按照指定的要求去做,那么就可以得到天气信息;
支付宝之前只有淘宝有,很多网站现在都有,因为也实现了接口,所以现在基本上就是面向接口编程;
麻雀会飞, 鹦鹉会飞 鸵鸟不会飞 企鹅不会飞 直升机会飞
用多态来实现虚方法,抽象类,接口;
IFlyable fly = new YingWu()//new MaQue()//new Plane();
fly.Fly();
public class Bird
{
public double Wings
{
get;
set;
}
public void EatAndDrink()
{
Console.WriteLine("我会吃喝");
}
}
public class MaQue:Bird,IFlyable
{
public void Fly()
{
Console.WriteLine("麻雀会飞");
}
}
public class YingWu:Bird,IFlyable,ISpeakable
{
public void Fly()
{
Console.WriteLine("鹦鹉会飞");
}
public void Speak()
{
Console.WriteLine("鹦鹉可以学人类说话");
}
}
public class TuoBird:Bird
{
}
public class QQ:Bird
{
}
public class Plane:IFlyable
{
public void Fly()
{
Console.WriteLine("直升飞机转动螺旋桨飞行");
}
}
public interface IFlyable
{
void Fly();
}
public interface ISpeakable
{
void Speak();
}
//显示实现接口就是为了解决方法的重名问题;
public class Bird:IFlyable
{
public void fly()
{
Console.WriteLine("鸟会飞");
}
void IFlyable.Fly() //显示实现接口; 不允许加访问修饰符,类中的成员默认是private,接口中默认是public
{
Console.WriteLine("我是接口的飞"); //这个其实跟抽象类一样,调用的是父类的函数,只不过在子类中重写了;
}
}
public interface IFlyable
{
void Fly();
}
Bird自己的方法和接口中的方法重名了,
IFlyable fly = new Bird();
fly.Fly(); //接口的飞 本质上访问的是接口的Fly,只不过接口的Fly被子类实现了;接口的Fly是public的
Bird bird = new Bird();
bird.Fly(); //自己的飞
在我提供给你的这几个类当中,如果说你能抽象出一个父类,并且父类当中必须写上这几个子类共有的方法,然后你不知道如何去写这个方法,这个时候要用抽象类
反之,抽象出来的父类可以写,还需要创建这个父类的对象,这个时候用虚方法,
如果这几个类里面根本找不出父类,但是它们都有共同的行为,共同的能力,这个时候拿接口实现多态;
比如鸟和飞机没有父类,但是它们都会飞,这个飞只能写成接口,不能写个父类去继承,因为根本提不出父类,
//真的鸭子会游泳, 木头鸭子不会游泳,橡皮鸭子会游泳; 一看到会干什么,能干什么侧重于能力,能力就由接口来做,把真鸭子作为父类,但是不能在父类里面写游泳函数,因为木鸭子不会,不能写抽象方法,因为真的鸭子需要被创建 对象,真鸭子有意义,虚方法也不合适,因为木鸭子不会游泳,所以用接口最合适;
public class RealDuck:ISwimming
{
public void Swim()
{
Console.WriteLine("真的鸭子靠翅膀游泳");
}
}
public class MuDuck
{
}
public class XPDuck:ISwimming
//不写方法体也不会报错,因为继承于RealDuck了,RealDuck里面有Swim了,但是由于他们游泳的方式不一样,因此没必要继承RealDuck
{
public void Swim()
{
Console.WriteLine("橡皮鸭子漂着游泳");
}
}
public interface Iswimming
{
void Swim();
}
ISwiming swim = new RealDuck()//new XPDuck() ;
swim.Swim();
超市收银管理系统,商品类,仓库类,超市类;
类前面不加访问修饰符,默认是Internal表示同一个项目中可以访问;
第一步,写商品的父类ProductFather;写价格,数量,编号三个自动属性;再写一个父类的构造函数
GUID能够帮助我们产生全世界独一无二的编号,GUID是个结构, Guid.NewGuid().ToString();
第二步,写商品的子类继承父类;写出每个子类的构造函数;
第三部,收银系统中最重要的一个类,仓库类;
存储货物;
List<ProductFather> list1 = new List<ProductFather>(); //这样写表示把所有的商品都堆在一堆了,没有体现出分门别类存储商品;所有货物都混在一起,拿的时候不好拿;
List<List<ProductFather>> list2 = new List<List<ProductFather>>();//在集合里面又放了一个集合;List<ProductFather>这个是数据的类型;
在给list1添加数据的时候,可以把货物对象直接添加进来,坏处是取得时候很麻烦;取得时候不知道哪个下标是谁;看下标对应的是哪个对象;于是乎我们想到用两个集合;
在给list2添加数据的时候只能添加一个集合进去;其实这个集合就是货架;给list2添加进去的不是商品,而是货架;
不管是list1还是list2其实都是整个仓库;
用list1存数据相当于 把商品直接扔进了仓库,在list2中添加进来的是数据的集合,仓库里的货架是货物的集合,所以往仓库添加数据的时候,先添加货架;
List<ProductFather> list1 = new List<ProductFather>();
list1[0] list1[1] list1[2]这些就是货物;//这种不好;
List<List<ProductFather>> list2 = new List<List<ProductFather>>();//这段代码创建出了一个仓库;
list2[0] list2[1] list2[2]这些就是货架;
因为创建仓库的时候已经要有货架了,因此我们要创建仓库类的构造函数;
//list[0]存储宏基笔记本 list[1]存储三星手机 list[2]存储酱油 list[3]存储香蕉
public CangKu()
{
list.Add(new List<ProductFather>());
list.Add(new List<ProductFather>());
list.Add(new List<ProductFather>());
list.Add(new List<ProductFather>());
//如果要1000个对象,就要1000行,但是我们可以通过循环创建;
}
进货:
public void JinPros(string strType,int count)
{
for(int i = 0 ;i < count;i++)
{
switch(strType)
{
case "Acer":
list[0].Add(new Acer(Guid.NewGuid().ToString(),1000,"宏基笔记本"));
break;
case "SangSung":
list[1].Add(new SangSung(Guid.NewGuid().ToString(),2000,"三星手机"));
break;
case "JiangYou":
list[2].Add(new JiangYou(Guid.NewGuid().ToString(),10,"老抽酱油"));
break;
case "Banana":
list[2].Add(new Banana(Guid.NewGuid().ToString()20,"大香蕉"));
break;
}
}
}
//来货之后,我们首先创建仓库对象,然后创建Getpros方法,往里面放货物;
取货:
public ProductFather[] QuPros(string strType,int count)
{
ProductFather[] pros = new ProductFather[conut];
for(int i = 0;i < count; i++)
{
switch(strType)
{
case:"Acer":
pros[i] = list[0][0];
list[0].RemoveAt[0];
break;
case:"SangSung":
pros[i] = list[1][0];
list[1].RemoveAt[0];
break;
case:"JiangYou":
pros[i] = list[2][0];
list[2].RemoveAt[0];
break;
case:"Banana":
pros[i] = list[3][0];
list[3].RemoveAt[0];
break;
}
}
return pros;
}
List<List<ProductFather>> list2 = new List<List<ProductFather>>();//这种存储数据的方式是为了分类;
展示货物,
public void ShowPros()
{
foreach(var item in list)
{
Console.WriteLine(item[0].Name item.count item[0].Price);
}
}