面向对象设计原则:
1 开闭原则:对扩展开放,对修改(old Code)关闭
2 类的单一职责:每个类有且只有一个改变它的原因
3 优先使用组合而非继承: 避免耦合度过高
4 面向接口编程而非面向过程: 定义一个类的时候,先思考对外提供什么功能,定义一个对外的接口
5 依赖倒置: 依赖抽象代码,因为具体实现容易改变
6 接口隔离:尽量定义小而精的接口,类实现多个功能,继承多个接口
7 里式替换:父类可以被子类替换掉
8 迪米特法则 : 类之间数据传递越少越好
C#中抽象类与接口
相同点:
1.可被继承, 不可被实例化,都是抽象的
不同:
1.抽象类可以继承类或接口 接口只能继承接口
2.抽象成员在子类中的实现方式不同,abstract的要用override实现,interface的实现不用
3.抽象类可包含已实现的成员, 接口只能出现抽象的成员
4.接口可以实现多继承,抽象类只能被单继承,基类必须在接口之前
5.接口中的方法必须被子类实现,抽象类中的方法不用,抽象方法必须被实现
面向对象的三大特性:封装,继承,多态
封装:组织代码的过程
继承:对概念和代码的复用
继承中的构造函数:构造函数不被继承,子类创建对象的时候,先调用父类的构造函数,再调用自己的构造函数,在父类没有无参的构造函数的时候,子类通过Base关键字指定调用调用哪个父类的构造函数。
多态:隐藏、重写、重载
隐藏:在子类中使用new关键字实现隐藏父类的方法
重写:父类方法不适用或者父类的抽象方法,子类中必须重写。可以重写的方法:虚方法、重写方法、抽象方法。
虚方法:用virtual修饰的方法,子类中可以不重写,抽象类和抽象方法都是用abstract修饰,抽象方法必须出现在抽象类中,子类必须重写抽象方法。
接口
接口目的为了提高代码的复用性我们用接口来定义行为
定义一组规范的数据结构,C#中为类提供某些功能
接口不能添加Public外的访问修饰符,接口中所有成员默认Public。接口中不能包含字段和已实现的方法。只能包含属性、未实现的方法、事件。
命名规范:一般接口的命名以"I"开头
实现方式两种:
隐式实现:通过方法名实现方法,方法前加public
显示实现(少用):通过“接口.方法名”的形式实现。显示实现可以避免二义性,访问显示实现的接口要创建对应接口的类,来进行访问。
namespace 接口
{
//定义接口: 接口命名以大写I开头 , 后面单词首字母大写
//接口可以直接声明,但是不能被new
//接口中的成员也是抽象的
interface IMyInterface
{
void Add(int i, int j);
}
//接口是抽象的概念,创建接口对象,需要声明接口new子类
//接口可以被继承
//实现接口的方法不需要 override
class MyClass : IMyInterface
{
public void Add(int a, int b)
{
Console.WriteLine(a + b);
}
}
//抽象类,成员可以是非抽象的
abstract class MyAbstractClass
{
public abstract void Add(int i, int j);
}
class MyClass2 : MyAbstractClass
{
public override void Add(int i ,int j)
{
Console.WriteLine(i + j);
}
}
class Program
{
static void Main(string[] args)
{
IMyInterface myInterface = new MyClass();
myInterface.Add(2, 2);
}
}
}
显式实现接口和隐式实现接口
//定义接口: 接口命名以大写I开头 , 后面单词首字母大写
//接口可以直接声明,但是不能被new
//接口中的成员也是抽象的
interface IMyInterface
{
void Cal(int i, int j);
string Name { get; set; }
void Print();
}
interface ICal
{
void Cal(int i, int j);
}
//接口是抽象的概念,创建接口对象,需要声明接口new子类
//接口可以被继承
//实现接口的方法不需要 override
class MyClass : IMyInterface, ICal
{
private string name;
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
//隐式实现接口
public void Print()
{
Console.WriteLine(Name);
}
//显示实现接口
//想调用显式接口,需要声明父接口才可以调用
void IMyInterface.Cal(int i, int j)
{
Console.WriteLine(i + j);
}
void ICal.Cal(int i, int j) {
Console.WriteLine(i - j);
}
public void Cal(int a, int b)
{
Console.WriteLine(a * b);
}
}
Struct 结构体
我们要复用一些数据时可以使用结构体来直接定义数据的集合
泛型
将类型作为参数,在使用的时候再具体决定
泛型参数可以是多个,一般使用大写的T开头
反省参数一般可以使用在方法 类 委托等
namespace 泛型
{
class Program
{
static void Main(string[] args)
{
//泛型 C#2.0 推出的一种新机制 , 简化 面向对象开发代码
//栈
MyStack myStack = new MyStack();
myStack.Push(233);
myStack.Push(1);
Console.WriteLine(myStack.Pop());
Console.WriteLine(myStack.Pop());
}
}
class MyStack
{
static int cap = 4;
T[] intStack = new T[cap];
static int currentIndex = 0;
public T Pop()
{
currentIndex--;
return intStack[currentIndex];
}
public void Push(T i)
{
if(currentIndex >= intStack.Length)
{
cap *= 2;
T[] newStack = new T[cap];
for(int j = intStack.Length; j < intStack.Length; j++)
{
newStack[j] = intStack[j];
}
intStack = newStack;
}
intStack[currentIndex] = i;
currentIndex ++;
}
}
}
第二种写法
//类型: 不安全的栈 会有拆装箱操作
class MyStack2 {
public void Push(T t) { }
}
进行泛型约束:
约束条件为接口是,将泛型参数类型为接口或者接口的实现类
MyStack2 xixi = new MyStack2();
xixi.Push(new SubCard());
}
}
class Card
{
}
class SubCard : Card
{
}
//类型: 不安全的栈 会有拆装箱操作
class MyStack2 {
//将T约束为某一类型,或其子类型
public void Push(T t) where T:Card
{ }
}
使用接口作为泛型约束的实例
对任意可比较类型数据进行升序排序(冒泡)
class MyClass02 : IComparable
{
public int i;
public int CompareTo(object obj)
{
return i.CompareTo((obj as MyClass02).i);
}
}
//对任意可比较类型数据进行升序排序
class MyClass01
{
public T[] ArraySort(T[] t) where T : IComparable
{
if (t.Length > 1)
{
for (int n = 0; n < t.Length; n++)
{
for (int m = 0; m < t.Length - n - 1; m++)
{
//如果前者大于后者
if (t[m].CompareTo(t[m + 1]) >= 0)
{
T temp = t[m + 1];
t[m + 1] = t[m];
t[m] = temp;
}
}
}
}
return t;
}
}
class Program
{
static void Main(string[] args)
{
MyClass02[] c1 = new MyClass02[3];
c1[0] = new MyClass02();
c1[0].i = 11110;
c1[1] = new MyClass02();
c1[1].i = 230;
c1[2] = new MyClass02();
c1[2].i = 10;
MyClass01 c3 = new MyClass01();
c1 = c3.ArraySort(c1);
Console.WriteLine(c1[0].i);
Console.WriteLine(c1[1].i);
Console.WriteLine(c1[2].i);
}
}
泛型约束
1.引用类型约束
where T:class
引用类型用class表示约束,约束必须为一个类,可以是接口interface
2.值类型约束
where T:struct
int、char类型都是struct
3.构造函数类型约束
where T:new()
指定类型T必须有构造函数
4.转换类型约束
就是我们可以通过装箱或者强制类型转换成目标类型的 类型都可以用于类型参数传入。