1.抽象类和抽象方法
abstract和virtual
https://blog.csdn.net/jiangxinyu/article/details/6570232
抽象类的存在目的就是必须被继承。
无法创建抽象类的实例,抽象类不能被实例化。派生类中用override关键字实现。
抽象类比virtual更进一步,更加抽象
using System;
using System.Collections.Generic;
namespace test1
{
abstract public class Pet//抽象方法必须在抽象类中
{
public string _name;//重命名变量,也叫重构,一般下划线_表示私有
public Pet(string name)
{
_name = name;
}
abstract public void Speak();//抽象方法不需要实现
}
public class Dog : Pet
{
public Dog(string Name) : base(Name)
{
}
override public void Speak()
{
Console.WriteLine(_name+"汪汪汪");
}
}
public class Cat : Pet
{
public Cat(string P) : base(P)//调用基类中的构造函数初始化Name,更加简洁
{
}
override public void Speak()
{
Console.WriteLine(_name + "喵喵喵");
}
}
class Program
{
public static void Main(string[] args)
{
Pet[] pets = new Pet[] { new Dog("小狗"), new Cat("小猫") };
foreach( Pet pet in pets)//foreach循环 Pet是数据类型,pet是foreach的迭代变量,读取集合中每一个元素
{
pet.Speak();
}
Console.ReadKey();
}
}
}
2.密闭类和密闭方法
有些类不希望其他人通过继承来修改,如string类就用密闭类用sealed修饰。
有些方法不希望其他人重写该方法,就用密闭方法。、
如果一个基类方法不希望子类对其重写,可以不声明为virtual和abstract。如果某个派生类方法不希望其子类对其重写,则同时使用sealed和override进行修饰。
3.接口
接口是指定一组函数成员,而不实现他们的引用类型(有点像完全没有普通函数和数据的抽象类,比抽象类更加抽象。是引用类型,不是值类型。)
接口用来被实现
有{}就代表有实现
接口的强大之处在于类可以同时继承多个接口。当派生类既继承基类也继承接口时,派生类冒号之后先写基类,在写接口。
interface ICatchMice
{
void CatchMice();//默认public,不能添加访问修饰符
}
interface IClimb
{
void Climb();
}
abstract public class Pet
{
public string name;
abstract public void Speak();
public Pet(string Name)
{
name = Name;
}
}
class Cat : Pet,ICatchMice,IClimb//实现类继承和接口继承,接口是引用类型
{
public Cat(string Name) : base(Name)
{
}
public void CatchMice()
{
Console.WriteLine(name+"catch a mouse");
}
public void Climb()
{
Console.WriteLine("爬上树");
}
public override void Speak()
{
Console.WriteLine("喵喵喵 ");
}
}
class Program
{
static void Main(string[] args)
{
Cat c = new Cat("小黑");
ICatchMice ic = (ICatchMice)c;//接口是引用类型,这里将C强制转化为IcatchMice引用类型。
c.CatchMice();//通过对象调用,可以调用对象所定义的函数
ic.CatchMice();//通过接口调用,只能调用接口所定义的函数,即CatchMice有两种实现方法,实现的方式不同
c.Speak();
IClimb tree = (IClimb)c;
tree.Climb();
Console.ReadKey();
}
}
4.结构和类:
结构时值类型(在栈中),类是引用类型(在堆中)
结构不支持继承,但可以实现接口。类支持继承也支持接口
结构不能定义默认构造函数,编译器会自定义
结构的使用场合:由于分配内存快,作用域结束立即被删除,不需要垃圾回收,用于小型的数据结构,但值类型传递过程中会赋值,应该使用ref即引用类型参数来提高效率。即有些参数等用完即不再需要了,就最好用结构。
类:用于其他需要继承体系的场合。
C#中结构的使用:
struct Rectgle
{
public double length;
public double width;
public Rectgle(double x,double y)
{
length = x;
width = y;
}
public double RectArea()
{
return length * width;
}
}
class program
{
static void Main(string[] args)
{
Rectgle rect;
rect.length = 12;
rect.width = 5;
Console.WriteLine(rect.RectArea());
Rectgle rect2 = new Rectgle(12, 8);//实例化结构
Console.WriteLine(rect.RectArea());
Console.ReadKey();
}
}
5.所谓用new实例化类,结构,其实是用new创建对象,面对对象编程中只有创建了对象,才能进行操作。
6.静态类和静态成员。
静态成员:
标识为static的字段,方法,属性,构造函数,事件,就是静态成员。
静态成员将被类的所有实例共享,所有实例都访问同一内存位置,即公共的成员。
一个类中的静态成员和实例成员是分开存储的。
静态成员的访问可以直接通过类名访问,而一般的成员则需要通过对象访问,即用new创建一个对象,然后通过对象访问。
静态成员的生存期,静态成员独立于任何的实例,且即使没有实例也可以访问,其初始化语句在静态成员使用之前调用。
静态函数成员:
静态函数也独立于任何实例,没有实例也可以访问,静态函数不能访问实例成员,仅可以访问其他静态成员,但实例成员可以访问静态函数
静态构造函数:
静态构造函数专门用于初始化静态字段。
在引用任何静态成员之前调用,在创建任何实例之前调用。
a.与类同名,使用static,无参数,无访问修饰符
class Dog
{
static int num;
static Dog(){num=0}
}
b.
public class Pet
{
static int Num;
string name;
public Pet(string Name)
{
name = Name;
++Num;//每创建一个Pet对象,此构造函数调用一次,Num加1
}
static public void PrintNum()
{
Console.WriteLine(Num);
}
}
class program
{
static void Main(string[] args)
{
Pet dog = new Pet("小黑");
Pet cat = new Pet("大黄");
Pet.PrintNum();//静态函数直接类名加方法名调用。
Console.ReadKey();
}
}
静态类:
静态类不能创建实例,且默认是密封的,不能被继承。
若类只包含静态方法和属性且标识为static则为静态类。
可以为静态类构造静态构造函数。
静态类主要用于基础类库(如数学库)和扩展方法。
扩展方法:
a.直接修改源代码,添加新方法
b.如果不能修改源代码但也不是密闭的类,则使用派生子类扩展
c.如果以上条件都不满足,可以使用静态类扩展的方法。
静态类的扩展方法:
a.
public class Dog//假设Dog这个类无法更改了
{
}
static class PetGuide
{
public static void HowToFeed(this Dog dog)//静态类传入dog对象
{
}
}
class program
{
static void Main(string[] args)
{
Dog dog = new Dog();
dog.HowToFeed();//创建Dog类的实例对象,却能够通过Dog类访问HowToFeed方法
}
}
b.静态类扩展要求
扩展方法必须在静态类中定义,扩展方法本身为static方法,扩展方法的第一个参数类型为this+类名。
7.重载操作符:
装箱和拆箱:装箱即根据值类型的值,在堆上创建一个完整的引用类型对象(一般为Object类型),并返回对象的引用,是一种隐式转换(即将值类型转换成引用类型)。转化成引用类型后可以进行统一的操作(例作为函数的参数)和统一的存储(object[])
装箱的示例:
int i = 3;
object oi = null;
oi=i;//在堆中创建了新的对象,oi指向该对象,其包含的值为3
装箱的本质:
即在堆上创建了引用类型的副本,新创建的引用类型和原来的值类型相互独立。装箱后改变其中一个,另一个不发生变化。
拆箱:将装箱后的对象转换回值类型的过程,是一种显示转换。
int i=3;
object oi=i;
int j=(int)oi;
自定义转换:有点不理解?
为自己的结构或者类定义显式或者隐式的转换。使结构和类变成一个预期的相关类型。
隐式转换:implicit operator
abstract public class Pet
{
public string _name;//重命名变量,也叫重构,一般下划线_表示私有
public Pet(string name)
{
_name = name;
}
abstract public void Speak();
}
public class Dog : Pet
{
public Dog(string Name) : base(Name)
{
}
override public void Speak()
{
Console.WriteLine(_name + "汪汪汪");
}
public static implicit operator Cat(Dog dog)//隐式转换,没有函数名,其中的Cat是返回值类型,将类Dog转化为Cat
{
return new Cat(dog._name);//返回一个Cat对象,将狗的名字给Cat
}
}
public class Cat : Pet
{
public Cat(string P) : base(P)//调用基类中的构造函数初始化Name,更加简洁
{
}
override public void Speak()
{
Console.WriteLine(_name + "喵喵喵");
}
}
class Program
{
public static void Main(string[] args)
{
Dog dog = new Dog("小狗");
Cat cat = dog;//这里的dog相当于new Cat(dog.name)
cat.Speak();//运行发现猫咪的名字变为Jack,叫声为喵喵喵。调用的方法为Cat类中的Speak
Console.ReadKey();
}
}
显式转换:explicit operator
abstract public class Pet
{
public string _name;//重命名变量,也叫重构,一般下划线_表示私有
public Pet(string name)
{
_name = name;
}
abstract public void Speak();
}
public class Dog : Pet
{
public Dog(string Name) : base(Name)
{
}
override public void Speak()
{
Console.WriteLine(_name + "汪汪汪");
}
}
public class Cat : Pet
{
public Cat(string P) : base(P)//调用基类中的构造函数初始化Name,更加简洁
{
}
override public void Speak()
{
Console.WriteLine(_name + "喵喵喵");
}
public static explicit operator Dog(Cat cat)//显式,没有函数名,其中的Dog是返回值类型,将类Cat转化为Dog
{
return new Dog(cat._name);//返回一个Dog对象,将猫的名字给Dog
}
}
class Program
{
public static void Main(string[] args)
{
Cat cat = new Cat("小猫");
Dog dog=(Dog)cat;//显式转换,这里的(Dog)cat相当于new Dog(cat.name)
dog.Speak();//运行发现狗的名字变为Jack,叫声为汪汪汪。调用的方法为Dog类中的Speak
Console.ReadKey();
}
}
重载运算符:这部分也没有特别理解
利用现有的某种运算符,针对自定义类或者结构,定义某种运算操作即利用原有的运算符重新定义了一种运算操作。利用现有的运算符,简化自定义类型的操作。最好是该运算符和该操作,具有一定的相关性。比如要类A和类B结合产生C,一般需要定义一个类进行操作,但更简单的是用A+B=C
public static Dog operator +(Dog male,Dog female)//重载加法运算 Dog是返回值。+为二元操作符
{
...
return new Dog();
}
重载运算符中,一元运算符操作数必须是类或者结构。二元操作符两个操作数至少有一个是类或者结构。
重载运算符不能够做什么:
创建新运算符,改变运算符语法,改变运算符的优先级和结合性。
例如重载++一元运算符:
abstract public class Pet
{
public string _name;
public int _age;
public Pet(string name)
{
_name = name;
_age = 0;
}
abstract public void Speak();
public static Pet operator ++(Pet pet)//重载运算符++,使++的操作定义发生改变,其中Pet是返回值类型
{
++pet._age;
return pet;//此时返回的pet中_age已经改变了
}
public void ShowAge()
{
Console.WriteLine(_name + _age+"岁");
}
}
public class Dog : Pet
{
public Dog(string Name) : base(Name)
{
}
override public void Speak()
{
Console.WriteLine(_name + "汪汪汪");
}
}
public class Cat : Pet
{
public Cat(string P) : base(P)//调用基类中的构造函数初始化_name,更加简洁
{
}
override public void Speak()
{
Console.WriteLine(_name + "喵喵喵");
}
}
class Program
{
public static void Main(string[] args)
{
//Pet[] pets = new Pet[] { new Dog("小狗"), new Cat("小猫") };
//for (int i=0;i<pets.Length;++i)
// {
// pets[i]++; //_age改变
// pets[i].ShowAge();
// }
上面的一段相当于
Pet dog=new Dog("小狗");
dog++;
dog.ShowAge();
Pet cat = new Cat("小猫");
cat++;
cat.ShowAge();
Console.ReadKey();
}
}
8.泛型:
a.泛型类
泛型类是一个模子,装入类型的材料,就可以塑造出想要的产品。即将具体的类的类型和相关参数注入泛型类,就可以得到自己想要的具体的类。(相当于一个实参,实参是类类型或内置类型)
例如
class Cage<T>{ //这里创建一个Cage的模型,T是类类型(可以说是类型实参),T是占位符作用:会被替换掉
T[] petsArray;
public void PutIn(T pet){...}
public T TakeOut(int index){...}
}
为什么用泛型类:
减小工作量,用基类和接口也可以作为模板的作用,但有弊端,使类型太宽泛,什么都可以由此派生即放入,需要显式的转换类型,并且判断其真实类型是什么。而泛型类只有有需要的类型才能被实例化。易于维护,修改模板,所有实例都将改变。
实例化:
class Cage<T>{...}//泛型类的声明,声明Cage<T>泛型类
Cage<Dog> dogCage;//Cage<Dog>类型的引用,即创建名称为dogCage的Cage<Dog>类型
dogCage=new Cage<Dog>();//构造实例
例子:
abstract public class Pet
{
public string _name;//重命名变量,也叫重构,一般下划线_表示私有
public Pet(string name)
{
_name = name;
}
abstract public void Speak();
}
public class Dog : Pet
{
public Dog(string Name) : base(Name)
{
}
override public void Speak()
{
Console.WriteLine(_name + "汪汪汪");
}
}
public class Cage<T>
{
T[] array;//T类型数组,名称为array
readonly int Size;
int num;
public Cage(int n)
{
Size = n;
num = 0;
array = new T[Size];//初始化数组
}
public void PutIn(T pet)//传入T类型参数
{
if (num < Size)
{
array[num++] = pet;
}
else
{
Console.WriteLine("Cage is full");
}
}
public T TakeOut()//返回的参数类型是T类型
{
if (num > 0)
{
return array[--num];
}
else
{
Console.WriteLine("Cage is empty");
return default(T);
}
}
}
public class program
{
public static void Main()
{
var dogCage = new Cage<Dog>(1);//这是什么形式?
dogCage.PutIn(new Dog("A"));//new Dog("A")是作为T类类型,为Dog类创建实例
dogCage.PutIn(new Dog("B"));
var dog = dogCage.TakeOut();//var是什么类型?
Console.ReadKey();
}
}
var关键字:
var 是3.5新出的一个定义变量的类型 其实也就是弱化类型的定义 VAR可代替任何类型 编译器会根据上下文来判断你到底是想用什么类型的 至于什么情况下用到VAR 我想就是你无法确定自己将用的是什么类型 就可以使用VAR 类似 OBJECT 但是效率比OBJECT高点。
或者通俗的讲:
var可以理解为匿名类型,我们可以认为它是一个声明变量的占位符。它主要用于在声明变量时,无法确定数据类型时使用。
使用var定义变量时有以下四个特点:
1. 必须在定义时初始化。也就是必须是var s = “abcd”形式,而不能是如下形式: var s; s = “abcd”;
2. 一但初始化完成,就不能再给变量赋与初始化值类型不同的值了。
3. var要求是局部变量。
4. 使用var定义变量和object不同,它在效率上和使用强类型方式定义变量完全一样。
泛型方法:
方法的模型,泛型类中往往有泛型方法,普通类中也可以有泛型方法。
例子:
abstract public class Pet
{
public string _name;//重命名变量,也叫重构,一般下划线_表示私有
public Pet(string name)
{
_name = name;
}
abstract public void Speak();
}
public class Dog : Pet
{
public Dog(string Name) : base(Name)
{
}
override public void Speak()
{
Console.WriteLine(_name + "汪汪汪");
}
public void DogIsHappy<T>(T target)//<T>是传入参数的类型 (T target)是传入的参数。
{
Console.WriteLine("Happy:" + target.ToString());// 将T类型的target变成string类型然后输出
}
}
class Person
{
}
public class program
{
public static void Main()
{
Dog dog = new Dog("A");
dog.DogIsHappy<Person>(new Person());
dog.DogIsHappy<int>(3);
Console.ReadKey();
}
}
运行结果:
约束:约束可以控制泛型参数的范围,缩小泛型范围。通常泛型参数可以是引用类型可以是值类型,可以是自定义可以是预定义。只有添加约束才可以调用泛型参数(如T)中的方法,负责只能调用object的方法。
约束类型:类名(该类或者继承该类的类),class,struct,接口名(该接口类型或者任何实现该接口的类型),new()(带有无参共有构造函数的类)
约束叠加规则:A.主约束(类名,class,struct) 只能有一个 B.接口约束可以有任意多个 C.构造约束
例子:
void Cage<T>
where T1:Pet, IClimbTree, New()
{
}
添加了有默认构造函数的new()约束,则可以在程序中new出一个T类型对象。添加Pet类约束,可以调用Pet类所有方法。添加接口约束,则可以调用接口中声明的方法。此时泛型类的参数类型只能在这些约束中选择。
泛型接口:
示例:
abstract public class Pet
{
public string _name;//重命名变量,也叫重构,一般下划线_表示私有
public int _age;
public Pet(string name)
{
_name = name;
_age = 0;
}
abstract public void Speak();
public static Pet operator ++(Pet pet)//Pet是返回值类型
{
++pet._age;
return pet;
}
public void ShowAge()
{
Console.WriteLine(_name + _age + "岁");
}
}
public class Dog : Pet
{
public Dog(string Name) : base(Name)
{
}
override public void Speak()
{
Console.WriteLine(_name + "汪汪汪");
}
}
public abstract class DogCmd
{
public abstract string GetCmd();
}
public class SitDogCmd :DogCmd
{
public override string GetCmd()
{
return "sit";
}
}
public interface IDogLearn<C>where C :DogCmd//即这个接口可以调用DogCmd中的方法
{
void Act(C cmd);
}
public class Labra : Dog, IDogLearn<SitDogCmd>//传入SitDogCmd类型
{
public Labra(string name) : base(name)
{
}
public void Act(SitDogCmd cmd)
{
Console.WriteLine(cmd.GetCmd());
}
}
class program
{
public static void Main(string[] args)
{
Labra dog = new Labra("A");
dog.Act(new SitDogCmd());
Console.ReadKey();
}
}