类介绍
类的定义是以关键字 class 开始,后跟类的名称。
类属于引用类型,只能通过new方式创建。
如果类定义中没有指定基类,那其基类为system.object
// <访问修饰符> class class类名
<access specifier> class class_name
{
// 成员属性
<access specifier> <data type> variable1;
// 成员方法
<access specifier> <return type> methodN(parameter_list)
{
// method body
}
}
类的访问修饰符
无或internal: 只能在当前项目中访问类
public:可以在任何地方访问类
abstract或internal abstract:类只能在当前项目中访问,不能实例化,只能供继承使用
public abstract:类可以在任何地方访问,不能实例化,只能供继承使用
sealed或internal sealed:类只能在当前项目中访问,不能供派生使用,只能实例化
public sealed:类可以在任何地方访问,不能供派生使用,只能实例化
类成员的访问修饰符
所有类型和类型成员都具有可访问型级别简单来说就是当前函数或属性的访问权限。
public 整个解决方案中访问。
protected internal 是protected 和 internal 的并集,符合任意一条都可以访问。
proteced 只允许在当前类内部以及该类的子类中访问。
internal 只能在当前项目中访问。在同一个项目中与 public 的权限一致。
private 只允许在本项目本类中被访问。
静态成员(函数)
C#的类静态成员变量和类静态成员函数和c++一致。区别在于:c#可以在类的定义内部初始化静态变量,而c++不行。
字段和属性
C#字段相当于c++的成员变量,属性是一个类中的变量属性,可以在函数中访问和使用。并且保持私有性来实现封装。属性可以看成是通过访问器对字段进行访问。
class CPerson
{
private int age; // 字段
public int Age
{
get { return age; }
set { age = value; }
} // 属性
}
或者直接自动实现属性
class CPerson
{
public int Age { get; set; } // 属性
}
自动实现的属性可以使用属性初始化器来初始化:
class CPerson
{
public int Age { get; set; } = 1;// 属性
}
可以给属性的get和set设置不同的访问修饰符,但get和set必须有一个采用属性的访问级别。
如: public int Age { get; private set; }
方法
即是类的成员函数。
[访问修饰符] <返回值类型> <方法名> ([参数列表])
{
// 方法体;
}
和c++一样可以重载、重定义、虚函数。
表达式体方法
如果方法只有1条语句。不需要编写花括号和return关键字,而是使用“=>”,其左边为声明,右边为定义。
public bool isSquare(Rectangle rect) => rect.h == rect.w;
参数传递
当调用带有参数的方法时,您需要向方法传递参数。在 C# 中,有三种向方法传递参数的方式:值类型,引用类型,输出类型
值传递:写法和c++一样
引用类型:ref关键词指定,使用:1、调用函数变量必须非常量;2、调用变量必须初始化。
输出类型:可以返回多个值;关键字为out;
注:输出变量与引用变量的区别
1、未初始化的变量用作ref非法,而out合法;
2、函数调用out参数量,必须把它当作尚未赋值(即可以把已赋值的变量当作out参数,但存储在该变量中的值在方法执行时会丢失);
3、ref,out必须在调用及定义方法时声明;
in:表示向方法传递一个值类型,避免复制值的开销,同时又不想在方法内改变值。
个数可变的参数
方法1:声明数组类型的参数,添加 params 关键字,就可以使用任意数量的 int 参数调用该方法。
方法2:如果应该把不同类型的参数传递给方法,可以使用 object 数组。
如果 params 关键字与方法签名定义的多个参数一起使用,则 params 只能使用一次,而且它必须是最后一个参数。
class CPerson
{
public int add(int nIndex,params object[] data)
{
return 0;
}
}
静态构造函数
这种构造函数整个程序运行期间只执行一次,类有一些静态字段或属性,需要在第一次使用类之前,从外部源中初始化这些静态字段和属性。在 C# 中,通常在第一次调用类的任何成员之前执行静态构造函数(静态构造函数至多执行一次)。而c++没有静态构造函数的概念。
class CPerson
{
static CPerson()
{
number = 2;
}
public static int number;
}
多重继承
C# 不支持多重继承。但是,您可以使用接口来实现多重继承。C++支持多重继承。
调用方法的基类版本
在c#使用base关键字来调用.而在c++是 基类名::方法名
c#结构体和类的区别
第一点 存储空间
结构体跟类最大的区别在于存储空间,结构体是值,存在栈上,类是引用,存在堆上。
第二点 特性
面向对象思想中有三大特性,封装,继承,多态,结构体具备封装的特性,但不具备继承,多态的特性。
注意:在c++中,结构体支持继承。
第三点 初始值
结构体成员变量申明不能指定初始值,而类可以。
注意:在c++中,结构体可以变量申明指定初始值。
![](https://i-blog.csdnimg.cn/blog_migrate/fccfd9ec4f7cd420247cca2942905d22.png)
![](https://i-blog.csdnimg.cn/blog_migrate/65d8d20d576fbbf6e7f28aa256d63cb2.png)
第四点 protected访问修饰符
结构体成员不能使用protected访问修饰符
第五点 无参构造函数
结构体中不能申明无参的构造函数,而类可以、
注意:c++,结构体中能申明无参的构造函数
![](https://i-blog.csdnimg.cn/blog_migrate/75a81209ed14c733f3e88e4ddf7b5ca2.png)
第六点 有参构造函数
在类中申明有参构造函数后,无参构造函数会被顶掉,如果想使用无参构造函数,必须重写无参构造函数.
![](https://i-blog.csdnimg.cn/blog_migrate/59f2dff4292bb091a114be0e0ae2e6d6.png)
![](https://i-blog.csdnimg.cn/blog_migrate/89458a93f3f4ae853a7386b6d714070d.png)
结构体中申明有参构造函数后,无参构造不会被顶掉
![](https://i-blog.csdnimg.cn/blog_migrate/260db4424e63c70c37ffb7516861c2fc.png)
第七点 析构函数
结构体中不能申明析构函数,而类可以
注意:在c++结构体中能申明析构函数
![](https://i-blog.csdnimg.cn/blog_migrate/71f6ac7e413f9f01973d8c53492077cc.png)
第八点 继承
结构体不能被继承,而类可以
注意:在c++结构体能被继承
![](https://i-blog.csdnimg.cn/blog_migrate/327c36e186afe36ba3ee14828e211772.png)
第九点 初始化
结构体需要在构造函数中初始化所有成员变量,而类随意
![](https://i-blog.csdnimg.cn/blog_migrate/ace209048ce4fa0d5aa4afd4d3206f0c.png)
第十点 静态
结构体不能被静态static修饰,而类可以
![](https://i-blog.csdnimg.cn/blog_migrate/e268b420f8a0231e02512eeddeddfa90.png)
第十一点内部申明自己
结构体不能在内部申明和自己一样的结构体变量,而类可以
抽象类(abstract)
抽象类的用途是提供一个可供多个派生类共享的通用基类定义。
public abstract class school
{
public int a;
public void func1() { }
public virtual void getname() { }
}
public class myschool: school
{
public override void getname()
{
Console.WriteLine("myschool");
}
}
特点
- 包含抽象方法、虚方法、常规方法和属性。
- 抽象类不能实例化,只能被继承。
- 抽象方法没有实现,必须在派生类中被实现,否则派生类也必须声明为抽象类。
抽象方法自动是虚方法。
- 如果一个类有抽象方法,则该类就是抽象类,必须用abstract 声明。
- 抽象方法必须被声明为public,因为派生类必须可以访问它。
密封类(sealed )
密封类不能用作基类。它也不能是抽象类。 密封类禁止派生。
在对基类的虚成员进行重写的派生类上,方法、索引器、属性或事件可以将该成员声明为密封成员。在用于以后的派生类时,这将取消成员的虚效果。方法是在类成员声明中将 sealed 关键字置于 override 关键字前面
使用场景:供库,类或者自己编写的其他类的内部操作使用。
public sealed class D
{
// Class members here.
}
class CPerson
{
static CPerson()
{
number = 2;
}
public static int number;
public int Age { get; set; } // 属性
public virtual void func()
{
}
}
class computer: CPerson
{
public sealed override void func() { }
}
接口(interface)
接口是指定一组函数成员而不实现它们的引用类型.
public interface ISpecialLoad
{
void load();
}
public interface ILoad
{
void load();
}
public interface ISave
{
void save();
}
public interface IPerson: ILoad, ISave
{
//public int a;
void check();
string getname();
}
public class CMen : IPerson
{
public void check()
{
int a = 1;
}
public string getname()
{
return "a";
}
public void save()
{
int a = 2;
}
public void load()
{
int a = 3;
}
}
public class CWomen : ILoad, ISpecialLoad
{
public void load()
{
Console.WriteLine(" load ");
}
}
public class CStudent : ILoad, ISpecialLoad
{
void ILoad.load()
{
Console.WriteLine("ILoad load ");
}
void ISpecialLoad.load()
{
Console.WriteLine("ISpecialLoad load ");
}
void func()
{
((ILoad)this).load();//调用ILoad接口
}
}
特点:
- 接口可以包含实例方法、属性、事件、索引器或这四种成员类型的任意组合
- 不能包含构造函数。
- 不能包含字段。
- 成员不指定访问关键字(隐式是public)。
- 接口不能被实例化;
- 接口可以多继承。接口可以从一个或多个接口继承,因此接口包含它继承的所有接口和所有基接口的成员。
- 只有类或者结构才能实现接口,必须为接口的每一个成员(包括其基类的)提供实现。
- 如果一个类实现了多个接口,并且其中一些接口有相同签名和返回类型的成员,那么类可以实现单个成员来满足所有包含重复成员的接口。
- 显式接口成员实现。可以使用限定接口名称显式地为每一个接口分别进行实现。实现类中的显示接口成员实现只可以通过指向接口的引用来访问。
抽象类(abstract)和接口(interface)的关系
相同点
1、不能实例化;
2、包含未实现的方法声明
3、派生类必须实现未实现的方法,抽象类是抽象方法,接口则是所有成员(不仅是方法包括其他成员)
不同点
1、接口可以多继承,抽象类不能实现多继承。
2、接口只能定义抽象规则,抽象类既能定义抽象规则也能提供已实现的成员。
3、接口是一组行为规范,抽象类是一个不完全的类,着重于族的概念。
4、接口支持回调,抽象类不能实现回调,因为继承不支持。
5、接口只包含方法、属性、索引器、事件的签名,但不能定义字段和包含实现的方法,抽象类可以定义属性、字段、包含有实现的方法。
6、接口可以作用于值类型和引用类型,抽象类只能作用于引用类型(例如:Struct只能继承接口)。
7、抽象类应主要用于关系密切的对象,而接口则是适合为不相关的类提供通用功能。
8、接口着重于Can—Do关系类型,抽象类则偏重于IS—A式关系。
9、接口多定义对象的行为,抽象类多定义对象的属性。
10、如果要设计小而简练的功能块,则使用接口,如果要设计大的功能单元,则使用抽象类。
11、接口对应是面向对象编程思想的重要原则:面向接口编程。