如有错误你爬网线来打我啊
世界是由对象组成的
对象
一台汽车,一个螺母,一速光线等等。每一样定义都有各自的属性。它们都是一个个对象
在 C# 使用关键字 class 定义对象。
例如现在我需要给我的世界(编程的世界)定义三维坐标使得物体可以有地方放置
class Vector3
{
}
构造函数
构造函数 ,是一种特殊的方法。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数类型的不同来区分它们 即构造函数的重载。
它没有返回值,没有返回值,没有返回值。重要的事说三次
访问修饰符 class Vector3
{
访问修饰符 Vector3(参数列表)
{
}
}
析构函数
当对象结束其生命周期时(例如对象所在的函数已调用完毕),系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟了一片内存空间,delete会自动调用析构函数后释放内存)。
访问修饰符 class Vector3
{
~Vector3(参数列表)
{
}
}
字段
很好,现在我已经定义了一个 三维向量,现在我需要向这个对象说明它应该有XYZ坐标轴的数据class Vector3
{
float x,y,z;
}
现在这个三维向量已经包含了三维数据。当然它现在仅仅能够自己使用这些属性而已。
你可能会说直接使用 public 给它们添加 公开权限 就好。
class Vector3
{
public float x,y,z;
}
的确 三维向量 的数据并不需要多余操作只需要直接添加权限给它。
但是有些场合可能并不能直接获得/设置相应的数据
它可能需要经过运算才能被批准获得/设置值
如果你看了上一章的函数,可能萌生了使用函数来获得/设置对象的属性
class Vector3
{
float x,y,z;
public float GetX(){
return x;
}
public void SetX(float value){
x = value;
}
public float GetY(){
return y;
}
public void SetY(float value){
y = value;
}
public float GetZ(){
return z;
}
public void SetZ(float value){
z = value;
}
}
可以看到当你这样去写的时候将会非常繁琐。
属性访问器
在C#我们并不需要这样去做,我们应该做的应该是直接使用访问器对它进行访问
public class Vector3
{
float x,y,z;
public float X{
get{return x;}
set{x=value;}
}
public float Y{
get{return y;}
set{y=value;}
}
public float Z{
get{return z;}
set{z=value;}
}
}
public class _Main
{
static void Main(string [] args)
{
Vector3 pos = new Vector3();
//! 1
pos.X = 123;
//! 2
Console.WriteLine(pos.X);
}
}
1、设置属性。
set
设置访问器,它有一个内定的变量 value 这个变量可以获得赋值,即当前 value 为 123
访问修饰符 类型 名称
{
访问休息符 set{
代码块
变量 = value;
}
}
2、访问属性。
get
读取访问器,它一定要有
return
返回值,在返回前你可以进行任意的运算
访问修饰符 类型 名称
{
访问休息符 get{
代码块
return 返回值;
}
}
使用了访问器后代码就变得精简了。不像使用函数一样繁琐。
封装
对象有些方法是不应该被外部访问的。
例如我现在有一台车,我可以启动它,但是我不需要知道它在启动的过程中到底进行了那些操作。
这种情况可以使用 访问修饰符 对对象进行封装,使得外部不能够知道具体的实现细节
- public:所有对象都可以访问;
- private:对象本身在对象内部可以访问,如果不添加访问修饰符,那么这是默认的;
- protected:只有该类对象及其子类对象可以访问
- internal:同一个程序集的对象可以访问;
- protected internal:访问限于当前程序集或派生自包含类的类型。
如果不能理解可以实际操作一下
继承
现在我需要写两个类, 一个是人,一个是学生
可能你会直接这样写
class Person
{
string name;
string address;
long phone;
}
class Student
{
string name;
string address;
long phone;
}
但是,我在这里要说的是,你首先是一个人,然后才是一个学生
那么 Student 应该是包含了 Preson 的所有属性的,所以我们的 Student 可以直接继承 Preson 这个类
class Person
{
string name;
string address;
long phone;
}
class Student:Person
{
}
此时,Student 将会拥有 Preson 的所有属性、方法、字段
标准语法
class 名称:基类
{
}
注意,只能继承一个类。如果继承了类之后,当前类被称为 【子类/派生类】,被继承的类称为 【基类】
密封
有些类我们不希望它们被继承,那么可以使用关键字 sealed 对类或方法进行密封
class sealed Manager
{
}
对于密封类来说,表示不能继承该类
密封类不能作为基类被继承,但它可以继承别的类或接口
在密封类中不能声明受保护成员或虚成员,因为受保护成员只能从派生类进行访问,而虚成员只能在派生类中重写
由于密封类的不可继承性,因此密封类不能声明为抽象的,即 sealed 修饰符不能与 abstract 修饰符同时使用
既然可以设置为密封类,那么当然也可以设置为密封方法
但是,并不是所有方法都可以设置为密封方法,只有对基类的虚方法进行实现并提供具体的实现的方法才可以密封
密封后,如果继承该类,就不能重写该密封的方法。在密封方法中,sealed总是和override同时出现
class 名称:基类
{
访问修饰符 sealed override 返回值 名称(参数列表)
{
代码块
}
}
抽象
一个类不与具体的事物相联系,而只是表达一种抽象的概念。
如果我只知道它会有一个显示的函数,但是完全不知道应该如何去实现的时候,可以只声明接口不写实现
除了 添加了 abstract 外的都和正常的类一样
class abstract 名称
{
访问修饰符 abstract 返回值 名称(参数列表);
}
不能创建抽象实例
如果想要使用,必须继承实现全部抽象方法和属性后才能创建实例。实现的方法必须和抽象类声明的方法一致
访问修饰符 class abstract 名称
{
访问修饰符 abstract 返回值 名称(参数列表);
}
访问修饰符 class 名称:基类
{
访问修饰符 override 返回值 名称(参数列表)
{
}
}
虚函数
我希望有一个成员函数(类内的函数)每一次继承后它的实现都不一样。
当继承后它调用的是当前类新写的方法,而不是基类的方法。
可以使用关键字 virtual 对该方法进行修饰,重写时使用 override 对方法进行修饰。虚函数可以不重写
访问修饰符 class 名称
{
访问修饰符 virtual 返回值 名称(参数列表)
{
}
}
访问修饰符 class 名称:基类
{
访问修饰符 override 返回值 名称(参数列表)
{
}
}
虚方法前不允许有static,abstract,或override修饰符
虚方法不能是私有的,因此不能使用private修饰符
接口
每一种品牌的水龙头它的接口尺寸都应该一样,毕竟我们不可能因为要换一个品牌的水龙头就换掉水厂
为了所有水龙头的接口都可以和水厂的接口链接。使用 Interface 定义接口的规范
访问修饰符 Interface 名称
{
返回值 名称{get;}
返回值 名称{set;}
返回值 名称{get;get;}
返回值 名称(参数列表);
}
类是可以同时继承多个接口的。继承后所有接口和属性等都必须实现
统一后无论是买什么牌子的水龙头都可以严实合缝的链接上水厂的接口
重载运算符
如果现在有两个 Vector3 实例 ,我想对它进行相加
public class Vector3
{
float x,y,z;
public float X{
get{return x;}
set{x=value;}
}
public float Y{
get{return y;}
set{y=value;}
}
public float Z{
get{return z;}
set{z=value;}
}
public Vector3(){}
public Vector3(float x, float y,float z)
{
this.x = x;
this.y = y;
this.z = z;
}
}
public class _Main
{
static void Main(string [] args)
{
Vector3 pos = new Vector3(1,1,1);
Vector3 pos2 = new Vector3(2,2,2);
Vector3 pos3 = new Vector3();
pos3.X = pos.X + pos2.X;
pos3.Y = pos.Y + pos2.Y;
pos3.Z = pos.Z + pos2.Z;
}
}
这样子看起来好蠢!我或许可以将它改为调用函数?
public class Vector3
{
.....
public static Vector3 Add(Vector3 pos,Vector3 pos2)
{
Vector3 pos3 = new Vector3();
pos3.X = pos.X + pos2.X;
pos3.Y = pos.Y + pos2.Y;
pos3.Z = pos.Z + pos2.Z;
return pos3;
}
}
public class _Main
{
static void Main(string [] args)
{
Vector3 pos = new Vector3(1,1,1);
Vector3 pos2 = new Vector3(2,2,2);
Vector3 pos3 = new Vector3();
pos3 = pos3.Add(pos,pos2);
}
}
看起来好多了,但是我还可以更加简便,那就是重载 + 运算符
public class Vector3
{
.....
public static Vector3 operator+(Vector3 pos,Vector3 pos2)
{
Vector3 pos3 = new Vector3();
pos3.X = pos.X + pos2.X;
pos3.Y = pos.Y + pos2.Y;
pos3.Z = pos.Z + pos2.Z;
return pos3;
}
}
public class _Main
{
static void Main(string [] args)
{
Vector3 pos = new Vector3(1,1,1);
Vector3 pos2 = new Vector3(2,2,2);
Vector3 pos3 = new Vector3();
pos3 = pos + pos2;
}
}
看起来是不是好多了,语法更加清晰了
重载运算符语法
public static 返回值 operator+(类型 变量1,类型 变量2)
{
//代码块
return 返回值;
}
只有特定的运算符才能重载
部分类
部分类用partial 关键字修饰,允许把类、结构或接口放在多个文件中。一般情况下,一个类存储在单个文件中。但有时,多个开发人员需要访问同一个类,或者某种类型的代码生成器生成了一个类的某部分,所以把类放在多个文件中是有益的。
public partial class Vector3 : Vector2
{
}
public partial class Vector3
{
}
就算把类拆分类也只能继承一个类。如果有一个部分继承了类其他部分就不能继承了
部分方法
部分方法也可以是静态的,但它们总是私有的,且不能有返回值。它们使用的任何参数都不能是out参数,但可以是ref参数。部分方法还不能使用virtual、abstract、override、new、sealed和extern修饰符。
public partial class Vector3 : Vector2
{
partial void DoSomething();
}
public partial class Vector3
{
partial void DoSomething()
{
}
}