C#基础

一,C#解释
C#是微软公司发布的一种面向对象的、运行于.NET Framework之上的高级程序设计语言。并定于在微软职业开发者论坛(PDC)上登台亮相。
C#是一种安全的、稳定的、简单的、优雅的,由C和C++衍生出来的面向对象的编程语言。它在继承C和C++强大功能的同时去掉了一些它们的复杂特性(例如没有宏以及不允许多重继承)。C#综合了VB简单的可视化操作和C++的高运行效率,以其强大的操作能力、优雅的语法风格、创新的语言特性和便捷的面向组件编程的支持成为.NET开发的首选语言。
二,unity开发和VR开发为什么用C#?
C#完整的面向对象特性和诸多语法便捷性,
三,C#语言的特点
简洁的语法,面向对象设计,完备的安全性,良好的兼容性,支持快速开发,面向组件开发
四,VS注释
/* Desc:描述

  • version:版本
  • date:日期
  • author:作者
    */
    五,数据结构
    值类型
    int (short long) double bool char struct结构 enum枚举 byte float decimal

引用类型
1)引用类型可以派生出新的类型
2).引用类型可以包含null值
3).引用类型变量的赋值只复制对对象的引用,而不复制对象本身
4).引用类型的对象总是在进程堆中分配(动态分配)
string object class类 delegate 数组
六,命名规则
标识符是由一系列字符组成,其中包括大小写字母、数字、下划线(_)和@字符。标识符不能以数字开头,也不能包含空格。合法的标识符的例子如Welcome1, _value, m_inputField1和button7。7button这个名字不是一个合法的标识符,因为它以数字开头。input field也是一个不合法的标识符,因为它包含了一个空格。C#大小写敏感——大写和小写字母被认为是不同的字母,因此a1和A1是不同的标识符。
转义序列字符\n的作用:另起下一行的作用。
七常量与变量
变量:是指在程序的运行过程中随时可以发生变化的量
 常量:是指在程序在运行过程中不会发生变化的变量

常量:const 数据类型 常量名
变量:变量命名规则:字母 数字 _组成,其中数字不能开头。不能使c#中的关键字
变量数据类型:int double float string bool 等等
八,运算符
其中“-”作为负号时为一元运算符,其余都为二元运算符。
基本算术运算符有:+(加)、-(减或负号)、*(乘)、/(除)、%(取余)。这些基本算运算符的意义与数学中相应符号的意义是一致的。它们之间的相对优先级关系与数学中的也是一致的,即先乘除、后加减,同级运算自左向右进行。使用算术运算符要注意以下几点:
1。“%”是取余运算,只能用于整型操作数。表达式a%b的结果为a/b的余数。“%”的优先级与“/”相同。
2.当“/”用于两整型操作数相除时,其结果取商的整数部分,小数部分被自动舍弃。因此,表达式1/2的结果为0,这一点需要特别注意。

赋值运算符 = 将右边的值复制给左边

关系运算符 >(大于) >= (大于等于) ==(等于) < (小于) <=(小于等于) 值为bool

逻辑运算符 ! 非运算取反
&&逻辑与两真为真否则为假
||逻辑或两假为假否则为真
三目运算符 int a=3>2?5:6; 判断为真取5 判断为假取6

++”“–”这两个运算符都有前置和后置两种使用形式
无论写成前置或后置的形式,它们的作用都是将操作数的值增1(减1)后,重新写回该操作数在内存中的原有位置。所以,如果变量i原来的值是1,计算表达式i++后,表达式的结果为2,并且i的值也被改变为2。但是,当自增、自减运算的结果要被用于继续参与其它操作时,前置与后置时的情况就完全不同了。例如,如果i的值为1,则下列两条语句的执行结果是不一样的:
i++;
++i;
九数据类型转换
1、隐式转换

隐式转换就是系统默认的、不需要加以声明就可以进行的转换。隐式转换不要求在源代码中使用任何特殊语法,编译器自动执行隐式强制转换。在隐式转换过程中,编译器无需对转换进行详细检查就能够安全地执行转换。隐式强制转换也称为“扩展转换“,因为要将窄数据类型转换为宽数据类型,且还将确保不会在转换过程中丢失数据。(注意:(1)转换前后的类型必须相兼容 (例如:int和double  (2)、隐式数值转换实际上就是从低精度的数值类型到高精度的数值类型的转换,即小的类型转大的类型)

int a = 10;
double b = a;//隐式转换
2、显式转换

显式类型转换,又叫强制类型转换。与隐式转换正好相反,显式转换需要用户明确地指定转换的类型。显式转换包括所有的隐式转换,也就是说把任何系统允许的隐式转换写成显式转换的形式都是允许的。用()实现显示转换,这表示,把转换的目标类型名放在要转换的值之前的圆括号中。
long val = 30000;
int i = (int)val;//显式转换
提醒:
(1)、显式转换可能会导致错误。进行这种转换时编译器将对转换进行溢出检测。如果有溢出说明转换失败,就表明源类型不是一个合法的目标类型。无法进行类型转换。
(2)、强制类型转换会造成数据精度丢失。

3、通过方法进行类型转换
(1)、使用ToString()方法。所有类型都继承了Object基类,所以都有ToString()这个方法(转化成字符串的方法)。
int i=200;
string s=i.ToString();
//这样字符串类型变量s的值就是”200” 。
(2)、通过int.Parse()方法转换,参数类型只支持string类型。注意:使用该方法转换时string的值不能为NULL,不然无法通过转换;另外string类型参数也只能是各种整型,不能是浮点型,不然也无法通过转换 (例如int.Parse(“2.0”)就无法通过转换)。
int i;
i = int.Parse(“100”);
(3)、通过int.TryParse()方法转换,该转换方法与int.Parse()转换方法类似,不同点在于int.Parse()方法无法转换成功的情况该方法能正常执行并返回0。也就是说int.TryParse()方法比int.Parse()方法多了一个异常处理,如果出现异常则返回false,并且将输出参数返回0。
int i;
string s = null;
int.TryParse(s,out i);
bool isSucess=int.TryParse(“12”, out i);//输出值为12;True
bool isSucess=int.TryParse(“ss”, out i);//输出值为0;False
bool isSucess=int.TryParse("", out i);//输出值为0;False

(4)、通过Convert类进行转换,Convert类中提供了很多转换的方法。使用这些方法的前提是能将需要转换的对象转换成相应的类型,如果不能转换则会报格式不对的错误(即前提为面上要过得去例如string类型的“666”可以转换为整数型666,string类型的“666aaa”却转换不成整数型 )。
static void Main(string[] args)
{
float num1=82.26f;
int integer,num2;
string str,strdate;
DateTime mydate=DateTime.New;
//Convert类的方法进行转换
integer=Convert.ToInt32(num1);
str=Convert.ToString(num1);
strdate=Convert.ToString(mydate);
num2=Convert.ToInt32(mydate);
Console.WriteLine(“转换为整型数据的值{0}”,integer);
Console.WriteLine(“转换为字符串{0},str”);
Console.WriteLine(“日期型数据转换为字符串值为{0}”,strdate);
Console.ReadKey();
}
附:Convert类常用的类型转换方法

方法 说明
Convert.ToInt32() 转换为整型(int)
Convert.ToChar() 转换为字符型(char)
Convert.ToString() 转换为字符串型(string)
Convert.ToDateTime() 转换为日期型(datetime)
Convert.ToDouble() 转换为双精度浮点型(double)
Conert.ToSingle() 转换为单精度浮点型(float)
(5)、 实现自己的转换,通过继承接口IConventible或者TypeConventer类,从而实现自己的转换。
注意:以Int类型为例,int.Parse,Convert.ToInt和int.TryParse的比较

【1】.参数和适用对象不同
int.Parse的参数数据类型只能是string类型,适用对象为string类型的数据
convert.toInt参数比较多,具体可以参见最下面的重载列表
int.TryParse的参数只能是只能是string类型,适用对象为string类型的数据
【2】.异常情况不同

异常主要是针对数据为null或者为""的情况

Convert.ToInt32 参数为 null 时,返回 0;Convert.ToInt32 参数为 “” 时,抛出异常;int.Parse 参数为 null 时,抛出异常。; int.Parse 参数为 “” 时,抛出异常。int.TryParse()方法比int.Parse()方法多了一个异常处理,如果出现异常则返回false,并且将输出参数返回0。

【3】.返回值不同

int.TryParse与int.Parse和Convert.ToInt 在返回值的不同是返回bool类型。获取转换后的值是通过out result这个参数获取的。

4、使用AS操作符转换

使用AS操作符转换,但是AS只能用于引用类型和可为空的类型。使用as有很多好处,当无法进行类型转换时,会将对象赋值为NULL,避免类型转换时报错或是出异常。C#抛出异常在进行捕获异常并进行处理是很消耗资源的,如果只是将对象赋值为NULL的话是几乎不消耗资源的(消耗很小的资源)。

5、装箱和拆箱

装箱和拆箱在值类型和引用类型之间架起了一座桥梁,使得任何 value-type 的值都可以转换为 object 类型的值,反过来转换也可以。
装箱:装箱是指将一个值类型的数据隐式地转换成一个对象类型(object)的数据。执行装箱操作时不可避免的要在堆上申请内存空间,并将堆栈上的值类型数据复制到申请的堆内存空间上,这肯定是要消耗内存和cpu资源的。注意:在执行装箱转换时,也可以使用显式转换。

int i = 0;
object obj = i; //装箱:值类型转换为引用类型
int j = (int)obj; //拆箱:引用类型转换为值类型

拆箱:拆箱是指将一个对象类型的数据显式地转换成一个值类型数据。拆箱过程是装箱的逆过程,是将存储在堆上的引用类型值转换为值类型并赋给值类型变量。拆箱操作分为两步:一是检查对象实例,确保它是给定值类型的一个装箱值;而是将该值从实例复制到值类型变量中。
装箱拆箱注意事项:

装箱操作可以隐式进行,但拆箱操作必须显示。
在装箱的时候,并不需要显示类型转换。但在拆箱时需要类型转换。
这是因为在拆箱时对象可以被转换为任何类型。
装什么拆什么
装箱就是要在托管堆重开辟空间,不但要装数值而且还要装类型
所以说装什么拆什么,也就是用什么值类型装箱,就要用什么值类型拆箱。

装箱和拆箱都是要消耗内存和cpu资源的,也就造成效率降低,所以要尽量避免使用。

装箱拆箱应用——定义方法

static void add(object i)
{
……
}
static void Main(string[] args)
{

short s = 10;
           int i = 100;
        	long l = 1000;

        	add(s);
        	add(i);
        	add(l);        
    }

注:可以理解为我定义一个object类型,好多小类型可以赋值过来。
十流程控制
C#程序的3种基本结构
顺序
选择
循环

选择:
1)If语句的三种形式
1.If(条件语句){}
2.If(条件语句){}
else {}
3.If (){}
else if{}
…………
else if{}
else {}
2)Switch (变量)
{
case 常量 : ……; break;
case 常量 : ……; break;
…………
default: ……… break;

}

循环:
1)While(表达式){……………}
2)do
{ sum = sum + i;
i = I + 1;
} while (I <= 100);
3)for(表达式1;表达式2;表达式3){ 语句组(循环条件)}

具体含义
for (循环变量赋初值;循环结束条件;循环变量增值)
{
}
4)循环嵌套:一个循环体内嵌套着另外一个完整的循环体称为循环的嵌套
格式 for(;;)
{
for(;;)
{
}
}
break 只能退出一层循环或switch语句
Continue 作用是结束本次循环,即跳过循环体中下面未执行的语句
十一数组
数组是同一类型的一组值,在内存中顺序存放
整个数组公用一个名字,而其中的每一项又称为一个元素。
int a[10] = {1,2,3,4,5} 等同于 int a[10] = {1,2,3,4,5,0,0,0,0,0}
int a[] = {1,2,3,4,5,6,7,8};
二维数组本质上是以数组作为数组元素的数组,即“数组的数组”。
int[,] arrray1=new int[2,5]{{9,9,9,9,0},{0,0,9,0,0}};  
数组有下标 下标从0开始
数组有长度length 长度为元素个数
九,函数(方法)
<访问修饰符> <返回类型> <方法名>(参数列表) {方法主体}。
比如:
public void Funtion(int a)
{ }
public int Funtion(int a)
{ }
十二访问修饰符
Private:用来限定的成员成为私有成员,对私有成员限定在该类的内部使用,即
只允许该类中的成员函数使用私有的成员数据,对于私有的成员函数,只能被该类
内的成员函数调用

Public:用来限定的成员成为公有成员,公有成员的数据或函数不受类的限制,可
以在类内或类外自由使用

Protected :用来限定的成员被称为保护成员,只允许在类内及该类的派生类中使用
保护的数据和函数,受保护成员的作用域,是该类和该类的派生类

internal:内部成员同一程序集内可访问。

Sealed:密封类:sealed关键字修饰

static 是标记静态函数(方法或属性)的关键字,静态函数(方法)或属性,不需要实例化对象就可以直接调用

静态方法与非静态方法:
1、内存中存储
静态的只有一块全局内存空间,非静态的可以有多块内存空间(副本)
2、都有哪些静态的东西
方法、字段、属性、事件、构造函数、类
不能使用静态关键字的
常量、索引器、析构函数
3、静态构造函数
非静态类中可以包含静态成员。但是,静态类中不能包含任何非静态成员
静态构造函数不能带有任何参数,而非静态构造函数可以有多种参数列表
4、使用场合
a、需要保存全局都有效的数据, 如:当前已登录用户信息、系统配置信息、系统设置
b、因为效率相对更高,所以需要快速访问的
c、使用频率非常高的
注意:不要过多的滥用Static

Const和readonly
一.值的区别:
常量(const):是已知的,不能修改的值。const都是静态的,不能使用static修饰。
只读字段(readonly):不能修改的,只读字段的值不能在编译时确定,而是在运行时确定的。
readonly可以用static修饰,也可以不用
二.赋值方法区别:
常量(const):只能在声明时赋值,常量的值在编译时就已经确定,在程序中不能改变。
只读字段(readonly):只读字段可以在声明时或者在构造函数内赋值。只读字段可以是静态字段(一个类只有一个值),也可以是实例字段(每一个实例有自己的值)。
Const 定义的是静态常在对象初始化的时候赋值.const修饰的常量必须在声明的同时赋值,以后不能改变它的值.属于编译时常量。不能用new初始化。

const修饰的常量为静态变量,不能够为对象所获取
Readonly 是只读变量.属于运行时变量.可以在类constructor里改变它的值.不能作用于局部变量。
const既可用来修饰类中的成员,也可修饰函数体内的局部变量;readonly只可以用于修饰类中的成员.
十三类和命名空间
定义:namespace 空间名{}
namespace name
{

}
命名空间不等于程序集,一个程序集可以有多个命名空间
using 命名空间指令导入包含在给定命名空间中的类型
引用自己定义的命名空间
using name;
定义:
类描述了一组有相同特性(属性)和相同行为(方法)的对象的抽象。
类是一种复杂的数据类型,它是将不同类型的数据和与这些数据相关的运算封装在一起的集合体
类将一些数据及与数据相关的函数封装在一起,使类中的数据得到很好的””保护”
在大型程序中不会被随意修改

Class 类名
{
private : 成员数据 ;成员函数;
public : 成员数据; 成员函数;
protected: 成员数据;成员函数;
}
类的修饰符
none or internal 类只能在当前工程中访问
public 类可以在任何地方访问
abstract or internal abstract 类只能在当前工程中访问,不能实例化,只能继承
public abstract 类可以在任何地方访问,不能实例化,只能继承
sealed or internal sealed 类只能在当前工程中访问,不能派生,只能实例化
public sealed 类可以在任何地方访问,不能派生,只能实例化
类可以声明为 static 的,以指示它仅包含静态成员。
使用静态类来包含不与特定对象关联的方法,具有通用性
1) 不能使用 new 关键字创建静态类的实例;
(2) 仅包含静态成员;
(3) 不能被实例化;
(4) 密封的,不能被继承;
(5) 不能包含实例构造函数,但可以包含静态构造函数;

创建类型的对象语法:
A 创建对象
类名 对象名=new 类名();
B 调用属性
对象名.属性名
C 调用方法
对象名.方法名()

方法重载:在同一个类中,方法名相同,参数列表不同(个数、类型、顺序),与返回值无关。
如何调用不同参数类型的重载方法:取决于传入的参数。

属性
概念:用于访问类的字段的成员
 属性用途:保证数据安全,作数据的验证
声明:
访问修饰符 数据类型 属性名
{
  get{return 字段;}  //读访问器,通过它外部用户可以读取属性的值
  set{字段=value;}  //写访问器,通过它外部用户可以为属性赋值,用户输入的值就存放在value关键字中,并可以进行输入值验证   }
十四,结构体和枚举
将不同种类的数据有序的组合到其一,构成一个新的数据类型,这种形式称为结构体
结构体是多种类型组合的数据类型
Struct 结构体名 {成员列表…}
Struct student {int num ; char name ; char sex ;char address} ;
struct stru {char k;int index;string name;};

1.结构体的含义
结0构体就是一堆数据类型不同但互有联系的数据的集合

2.结构体的声明
方式1 :先定义结构在声明 变量(比较常用)

struct Student
{
int num;
char name[30];
float score;
};
Student stud1,stud2;
方式2 : 定义结构同时声明 变量(比较常用)

struct Student
{
int num;
char name[30];
float score;
}stud1,stud2;
方式3:定义结构的时候省去结构名(不要去用)

struct {
int num;
char name[30];
float score;
}stud1,stud2;
结构体标识符 结构名
{
类型说明符 成员名;
};
注意:
1.结构体构造末尾别忘了;
2.结构体里面可以嵌套结构体

3.结构体的初始化
1.相同类型的结构体变量之间可以相互赋值,不同类型之间禁止
2.可以通过结构体成员单独进行赋值
3.可以统一进行赋值如:stud1={1010,“zhang san”,89.5};

4.结构体的调用
结构体成员的调用:stud1.num

5.结构体数组
struct Student
{
int num;
char name[30];
float score;
}stud[5]={{1010,“zhang san”,95.2},
{1011,“li si”,85.5},
‘’’’’’’’
};
6.指向结构变量和数组的指针
定义方式: struct 结构体名 * 指针名变量名
struct student *sp;
sp=stud; (stud是结构体数组)
sp=&stud; (stud是结构体变量)
sp=&student;(错误,不能是结构体名)
引用指针指向的机构体变量成员方式:(*sp).num;
或者是 sp->num (一般采用这种方法,上一种用的少)
struct stu{
char a;
int b;
float c;
}name={3,1000,99.8},*sp;
sp=name; //财务,name它是一个变量,sp是一个指针,是地址;
sp=&name; //正确,name它是一个变量,sp是一个指针,是地址;
(*sp).a和name.a完全等价, 和sp->a完全等价
(*sp).b和name.b完全等价, 和sp->b完全等价
(*sp).c和name.c完全等价, 和sp->c完全等价
注意:在使用结构体数组的时候,每个结构体变量是一个整体,里面的单个成员变量不属于结构体数组元素,若结构体数组使用++或–符号,是整个结构体变量变化,而不是成员变化

7.结构体变量作为函数参数
由于结构体复杂且占用内存空间大,若通过实参形参值传递开销较大,影响效率
所以一般采用的是指针传递
例子

`#include <stdio.h>
#include <string.h>

struct student {
int age;
char sex;
char name[30];
};

void inputstudent(struct student *ps)//对结构体变量输入时必须传地址
{
(*ps).age = 10;
strcpy(ps->name, “张三”);
ps->sex = ‘f’;
}

void outputstudent(struct student *ps)//对结构体变量输出时,可传地址也可传内容,但为了减少内存耗费,提高运行速度,建议使用传值
{
printf("%d %c %s\n", ps->age, ps->sex, ps->name);
}
int main()
{
struct student st;
inputstudent(&st);
outputstudent(&st);
return 0;
}
8.共用体
共用体就是多个不同类型数据共用一个存储空间,这个是以前为应对内存空间不足的情况。现在内存空间充足,不要用共用体,比较麻烦,也不影响赚钱;
这里简单说一下定义方法和赋值调用方法

union data
{
char a;
int b;
float c;
};
data hu;
hu.a=‘5’;
hu.b=52;
hu.c=20.9; //定义及初始化
9.枚举型
枚举就是穷举,在枚举值表中列举出数据的所有可能性,枚举是一种基本数据类型
不同于数组,结构体,共用体这类构造类型,不能够再分解;
定义方法

enum 枚举名{枚举值表}
enum weekday{mon,tue,wed,thu,fri,sat,sun}a,b,c;
枚举类声明有三种,和结构体类似,我这里就用上面这种(只用这种就够了)
注意:
1.枚举类型中,枚举值表是常量,不是变量;赋值语句不能给其赋值;
2.系统自动给其赋值,从0开始的正整数;也可以我们在声明时候在列表给其赋值;
例子:weekday a,b;
a=0;
b=0;
或者a=b; // 错误的,虽然编译系统能通过,但很容易出现bug
a=(enum weekday)2 ; //正确用法
十五封装
个人理解:封装就是用修饰符来封锁权限
封装 被定义为"把一个或多个项目封闭在一个物理的或者逻辑的包中"。在面向对象程序设计方法论中,封装是为了防止对实现细节的访问

1、封装,是面向对象程序设计最基本的特性。把数据(属性)和函数(操作)合成一个整体,这在计算机世界中是用类与对象实现的。
2、封装,把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
备注:有2层含义(把属性和方法进行封装 对属性和方法进行访问控制)

1 封装:将字段私有化(private),将字段进行封装,封装为属性(只读get、只写set、读写get ,set)。
2 创建类的步骤:
A 定义类名
B 定义字段(private)
C 封装字段(set get):双击字段,右键-》重构-》封装字段
D 构造方法(无参 有参)
E 定义方法

封装的优点
A.良好的封装能够减少耦合(比如实现界面和逻辑分离)
B.可以让类对外接口不变,内部可以实现自由的修改
C.类具有清晰的对外接口,使用者只需调用,无需关心内部
D.因为封装的类功能相对独立,因此能更好的实现代码复用
E.可保护代码不被无意中破坏,通过私有字段等实现内部。注意:这里的代码保护不是指代码本身的加密,而是对不想外部更改的代码通过私有实现。
十六继承
类可以从其他类中继承。这是通过以下方式实现的:在声明类时,在类名称后放置一个冒号,然后在冒号后指定要从中继承的类(即基类)
虚方法
C#中的虚方法使用virtual关键字定义
作用:子类可以对父类中的虚方法进行重写,虚方法是多态特性的一种体现
override关键字作用是对父类的virtual方法进行重写

在子类中用override重写父类中用virtual申明的虚方法时,实例化父类调用该方法,执行时调用的是子类中重写的方法;
如果子类中用new覆盖父类中用virtual申明的虚方法时,实例化父类调用该方法,执行时调用的是父类中的虚方法;

base关键字
  其用于在派生类中实现对基类公有或者受保护成员的访问,但是只局限在构造函数、实例方法和实例属性访问器中,MSDN中小结的具体功能包括:
  • 调用基类上已被其他方法重写的方法。
  • 指定创建派生类实例时应调用的基类构造函数。

this关键字
  其用于引用类的当前实例,也包括继承而来的方法,通常可以隐藏this,功能主要包括:
  • 限定被相似的名称隐藏的成员
  • 将对象作为参数传递到其他方法
  • 声明索引器  
十七多态
多态性是指”多种行为”同样的方法调用后,执行不同的操作、运行不同代码
多态性可以简单的概括为“一个接口,多种方法”,程序在运行时才决定调用的函数,它是面向对象三大特性之一,也是面向对象领域的核心概念

C+#多态性通过 虚函数 (virtual )来实现,虚函数允许子类重新定义成员函数,子类重新定义父类的做法叫做 覆盖,或者 重写。
重写分为两种:直接重写成员函数和重写虚函数,只有重写了虚函数才更好的体现C#的多态
重写和重载的区别:
重载是函数名相同,但是参数个数和类型不能完全相同,可以同时处于一个类的内部

重写是函数名 和参数完全相同,只不过内部实现不同,不能同时处于一个类的内部

1 多态:一个方法在不同类当中的体现。
2 构成多态的条件:a 继承 b 子类重写父类的方法
3 父类 对象名=New 子类();
对象名.方法名(); //调用子类的方法
多态的三个条件:
要用继承
要有函数重写
用父类指针(引用)指向子类的对象。

抽象:
1关键字:abstract:抽象
2由abstract修饰的类叫做抽象类,由abstract修饰的方法叫做抽象方法。
3如果一个类中有抽象方法,该类必须为抽象类。
4 抽象方法:一个方法如果是抽象方法,该方法必须在抽象类中。同时抽象方法只有方法的声明,没有方法实现。抽象方法由继承抽象类的子类去实现,即如果一个类继承了抽象类,该类必须实现抽象类中的所有抽象方法。重写抽象方法用override。
5 抽象类不能实例化对象。

虚方法和抽象方法都可以供派生类重写,它们之间有什么区别呢?

  1. 虚方法必须有实现部分,抽象方法没有提供实现部分,抽象方法是一种强制派生类覆盖的方法,否则派生类将不能被实例化。
  2. 抽象方法只能在抽象类中声明,虚方法不是。其实如果类包含抽象方法,那么该类也是抽象的,也必须声明为抽象的。
  3. 抽象方法必须在派生类中重写,这一点跟接口类似,虚方法不必
    十七接口
    1 定义:interface
    2 一组规范,特殊的抽象类(其中的方法全部是抽象方法),接口中的方法只有声明,没有实现而且无abstract 和访问修饰符。默认就是公有的(public)。接口可以实现多重继承。
    3 如果一个类既继承了类,有实现了接口,对应着继承的基类必须实现的接口的前面

1.接口类似于抽象基类,不能直接实例化接口;接口中的方法都是抽象方法,实现接口的任何非抽象类型都必须实现接口的所有成员:
当显式实现该接口的成员时,实现的成员不能通过类实例访问,只能通过接口实例访问。
当隐式实现该接口的成员时,实现的成员可以通过类实例访问,也可以通过接口实例访问,但是实现的成员必须是公有的。
2.接口不能包含常量、字段、运算符、实例构造函数、析构函数或类型、不能包含静态成员。
3.接口成员是自动公开的,且不能包含任何访问修饰符。
4.接口自身可从多个接口继承,类和结构可继承多个接口,但接口不能继承类。

接口的继承:
接口继承和类继承不同:首先,类继承不仅是说明继承,而且也是实现继承;而接口继承只是说明继承。
也就是说,派生类可以继承基类的方法实现,而派生的接口只继承了父接口的成员方法说明,而没有继承父接口的实现,
其次,C#中类继承只允许单继承,但是接口继承允许多继承,一个子接口可以有多个父接口。
接口可以从零或多个接口中继承。从多个接口中继承时,用":“后跟被继承的接口名字,多个接口名之间用”,"分割。
被继承的接口应该是可以访问得到的,比如从private 类型或internal 类型的接口中继承就是不允许的。
接口不允许直接或间接地从自身继承。和类的继承相似,接口的继承也形成接口之间的层次结构

1.接口是一个引用类型,通过接口可以实现多重继承。
2. C#中接口的成员不能有new、public、protected、internal、private等修饰符。
3. 接口中只能声明"抽象"成员(所以不能直接下一步对接口进行实例化(即不能使用new操作符声明一个接口的实例对 象)),而不能声明共有的域或者私有的成员变量。
4. 接口声明不包括数据成员,只能包含方法、属性、事件、索引等成员。
5. 接口名称一般都以“I”作为首字母(当然不这样声明也可以),这也是接口和类的一个区别之一
6. 接口成员的访问级别是默认的(默认为public),所以在声明时不能再为接口成员指定任何访问修饰符,否则 编译器会报错。
7. 接口成员不能有static、abstract、override、virtual修饰符,使用new修饰符不会报错,但会给出警告说不需要关键字new。
8. 在声明接口成员的时候,不准为接口成员编写具体的可执行代码,也就是说,只要在对接口进行声明时指明接口的成员名称和参数就可以了。
9. 接口一旦被实现,实现类必须实现接口中的所有成员,除非实现类本身是抽象类(通过具体的可执行代码实现接口抽象成员的操作)。

C#中的接口和类有什么异同。
异:
不能直接实例化接口。
接口不包含方法的实现。
接口可以实现多继承,而类只能是单继承。
类定义可在不同的源文件之间进行拆分。
同:
接口、类和结构可从多个接口继承。
接口类似于抽象基类:继承接口的任何非抽象类型都必须实现接口的所有成员。
接口可以包含事件、索引器、方法和属性。 一个类可以实现多个接口。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卿屿­­­­­­­-轻尘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值