推荐阅读:
- 我的CSDN
- 我的博客园
- QQ群:704621321
时隔半年没用C#了,最近打算回来巩固一下,下面就着重一些主要的,易忘的,难点的知识点带领大家一起回顾这个语言。
打开VS编辑器,文件-新建-项目-控制台应用程序-命名为“CSharp学习”-打开项目如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CSharp学习
{
class Program
{
static void Main(string[] args)
{
}
}
}
原则来说一个 C# 程序主要包括以下部分:
(1)命名空间声明(Namespace declaration)
(2)一个 class
(3)Class 方法
(4)Class 属性
(5)一个 Main 方法
(6)语句(Statements)& 表达式(Expressions)
(7)注释
完善程序如下:
using System;
namespace HelloWorldApplication
{
class HelloWorld
{
static void Main(string[] args)// C# 程序的 入口点
{
/* 我的第一个 C# 程序*/
Console.WriteLine("Hello World");
Console.ReadKey();//使得程序会等待一个按键的动作,防止程序从 Visual Studio .NET 启动时屏幕会快速运行并关闭。
}
}
}
注意:在任何 C# 程序中的第一条语句都是:using System;
下面给大家介绍一个技巧:输出语句的快捷键:“cw” + Tab + Tab
值类型(Value types):从类 System.ValueType 中派生的,比如 int、char、float。
引用类型(Reference types):指的是一个内存位置,内置的引用类型有:object、dynamic、string。用户自定义引用类型有:class、interface 或 delegate
指针类型(Pointer types):存储另一种类型的内存地址
对象(Object)类型:当一个值类型转换为对象类型时,则被称为 装箱;
object obj;
obj = 100; // 这是装箱
当一个对象类型转换为值类型时,则被称为 拆箱。
这里有个常见的笔试题:
装箱:将值类型转换为引用类型。
拆箱:将引用类型转换为值。
Console.ReadLine();
sizeof()
返回 class 的类型
typeof()
可以用来替代 if…else 语句
格式:Exp1 ? Exp2 : Exp3;
说明:? 表达式的值是由 Exp1 决定的。如果 Exp1 为真,则计算 Exp2 的值,结果即为整个 ? 表达式的值。如果 Exp1 为假,则计算 Exp3 的值,结果即为整个 ? 表达式的值。
例如:
int a=1;
int b=2;
int c;
a>b?c=a+b;c=b-a;
格式:foreach (int element in arr){}
把一个或多个项目封闭在一个物理的或者逻辑的包中,通过 访问修饰符实现。
访问修饰符分为:
public:所有对象都可以访问;
private:对象本身在对象内部可以访问;
protected:只有该类对象及其子类对象可以访问
internal:同一个程序集的对象可以访问;
protected internal:访问限于当前程序集或派生自包含类的类型。
(1)值参数:这种方式复制参数的实际值给函数的形式参数,实参和形参使用的是两个不同内存中的值。在这种情况下,当形参的值发生改变时,不会影响实参的值,从而保证了实参数据的安全。
(2)引用参数:这种方式复制参数的内存位置的引用给形式参数。这意味着,当形参的值发生改变时,同时也改变实参的值。在 C# 中,使用 ref 关键字声明引用参数
(3)输出参数:这种方式可以返回多个值。
下面就引用参数举个例子:
定义一个方法:
public void swap(ref int x, ref int y)
{
int temp;
temp = x; /* 保存 x 的值 */
x = y; /* 把 y 赋值给 x */
y = temp; /* 把 temp 赋值给 y */
}
调用该方法,实现a与b的值互换:
swap(ref a, ref b);
大家都知道return 语句可用于只从函数中返回一个值,那么需要返回多个值时,就得用到第三种类型了。特别的:当需要从一个参数没有指定初始值的方法中返回值时,输出参数特别有用
下面举个例子:
定义一个方法
public void getValues(out int x, out int y )
{
Console.WriteLine("请输入第一个值: ");
x = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("请输入第二个值: ");
y = Convert.ToInt32(Console.ReadLine());
}
调用该方法:
int a , b;
/* 调用函数来获取值 */
n.getValues(out a, out b);
下面来说说一个经常出现的面试题:
同:当一个方法需要返回多个值的时候,就需要用到ref和out。
异:ref 要求变量必须在传递之前进行初始化。
结论:关键字“ref“和”out”之间的唯一区别就是关键字”out“不要求调用代码初始化要传递的参数值。那么关键字‘ref”什么时候用呢?当您需要确保调用方法已经初始化参数值的时候,您就应该使用关键字“ref”。
下面举个例子来帮助大家更直观的理解:
double? num1 = null;//空
double? num2 = 3.14157;//3.14157
double? num3 = new double?();//空
double num4;
num4 = num1 ?? 5.34; // num1 如果为空值则返回 5.34
由于num1 为空,所以num4=5.34。
1.一维数组:
声明一个没有初始值的数组:
int[] num;
声明一个指定长度的数组:
int[] num= new num[10];
创建并初始化一个数组
方法1:
int num={1,2,3,4,5}
方法2:
int num=[5]{1,2,3,4,5}
方法3:
int num=[]{1,2,3,4,5}
2.多维数组:
声明一个二位数组:
int num[,]
创建并初始化一个二维数组
int[,] a = new int[5, 2] {{0,0}, {1,2}, {2,4}, {3,6}, {4,8} };
3.交错数组:数组的数组
声明:
int [][] scores
4.参数数组:当声明一个方法时,不确定要传递给函数作为参数的参数数目,使用 关键字:params
例如:
定义一个方法:
public int AddElements(params int[] arr)
{
int sum = 0;
foreach (int i in arr)
{
sum += i;
}
return sum;
}
使用该方法:
ParamArray app = new ParamArray();
int sum = app.AddElements(512, 720, 250, 567, 889);
不使用参数params时代码如下:
定义一个方法
public int AddElements(int[] arr)
{
int sum = 0;
foreach (int i in arr)
{
sum += i;
}
return sum;
}
使用该方法:
int[] num={1,2,3};
ParamArray app = new ParamArray();
int sum = app.AddElements(num);
5.Array 类:是 C# 中所有数组的基类,它是在 System 命名空间中定义
结构体是值类型数据结构,可以存储各种数据类型的相关数据。与类不同,结构不能继承其他的结构或类;结构可实现一个或多个接口;结构成员不能指定为 abstract、virtual 或 protected。
类和结构的不同点:
1.类是引用类型,结构是值类型。
2.结构不支持继承。
3.结构不能声明默认的构造函数。
4.结构体中声明的字段无法赋予初值,类可以。
类与结构的选择
首先明确,类的对象是存储在堆空间中,结构存储在栈中。堆空间大,但访问速度较慢,栈空间小,访问速度相对更快。故而,当我们描述一个轻量级对象的时候,结构可提高效率,成本更低。当然,这也得从需求出发,假如我们在传值的时候希望传递的是对象的引用地址而不是对象的拷贝,就应该使用类了。
枚举是值类型;枚举包含自己的值,且不能继承或传递继承
象是类的实例。构成类的方法和变量成为类的成员。
类的构造函数是类的一个特殊的成员函数,当创建类的新对象时执行。
默认的构造函数没有任何参数。如果需要一个带有参数的构造函数,这种构造函数叫做参数化构造函数。这种技术可以帮助你在创建对象的同时给对象赋初始值,例如:
lass Line
{
private double length; // 线条的长度
public Line(double len) // 参数化构造函数
{
Console.WriteLine("对象已创建,length = {0}", len);
length = len;
}
}
析构函数是在类的名称前加上一个波浪形(~)作为前缀,它不返回值,也不带任何参数。析构函数用于在结束程序(比如关闭文件、释放内存等)之前释放资源。析构函数不能继承或重载。
类的静态成员:当我们声明一个类成员为静态时,意味着无论有多少个类的对象被创建,只会有一个该静态成员的副本。例如:
声明一个类:
class StaticVar
{
public static int num;
public void count()
{
num++;
}
}
使用类中的count方法:
StaticVar s1 = new StaticVar();
StaticVar s2 = new StaticVar();
s1.count();//1
s1.count();//2
s1.count();//3
s2.count();//4
s2.count();//5
s2.count(); //6
已有的类被称为的基类,新的类被称为派生类。一个类可以派生自多个类或接口。C# 不支持多重继承,但是可以使用接口来实现多重继承,例如:
//基类
class Shape
{
public void setWidth(int w)
{
width = w;
}
public void setHeight(int h)
{
height = h;
}
protected int width;
protected int height;
}
// 接口
public interface PaintCost
{
int getCost(int area);
}
// 派生类
class Rectangle : Shape, PaintCost
{
public int getArea()
{
return (width * height);
}
public int getCost(int area)
{
return area * 70;
}
}
在子类中用 override 重写父类中用 virtual 申明的虚方法时,实例化父类调用该方法,执行时调用的是子类中重写的方法;
例如:
///
/// 父类
///
public class ParentClass
{
public virtual void ParVirMethod()
{
Console.WriteLine("父类的方法...");
}
}
///
/// 子类1
///
public class ChildClass1 : ParentClass
{
public override void ParVirMethod()
{
Console.WriteLine("子类1的方法...");
}
}
如果子类中用 new 覆盖父类中用 virtual 申明的虚方法时,实例化父类调用该方法,执行时调用的是父类中的虚方法
例如:
///
/// 父类
///
public class ParentClass
{
public virtual void ParVirMethod()
{
Console.WriteLine("父类的方法...");
}
}
///
/// 子类2
///
public class ChildClass2 : ParentClass
{
public new void ParVirMethod()
{
Console.WriteLine("子类2的方法...");
}
public void Test()
{
Console.WriteLine("子类2的其他方法...");
}
}
总结:override是重写,即将基类的方法在派生类里直接抹去重新写,故而调用的方法就是子类方法;而new只是将基类的方法在派生类里隐藏起来,故而调用的仍旧是基类方法。
分为:静态多态性和动态多态性。
C# 提供了两种技术来实现静态多态性。分别为:
1.函数重载:函数的定义不同,参数类型不同,参数个数不同。
2.运算符重载
动态多态性
动态多态性是通过 抽象类 和 虚方法 实现的。
使用关键字 abstract 创建抽象类,用于提供接口的部分类的实现。抽象类包含抽象方法,抽象方法可被派生类实现。
注意:
1.不能创建一个抽象类的实例。
2.不能在一个抽象类外部声明一个抽象方法。
3.通过在类定义前面放置关键字 sealed,可以将类声明为密封类。当一个类被声明为 sealed 时,它不能被继承。抽象类不能被声明为 sealed。
当有一个定义在类中的函数需要在继承类中实现时,可以使用虚方法virtual。虚方法可以在不同的继承类中有不同的实现。
例如:为用户自定义的类 Box 实现了加法运算符(+)。它把两个 Box 对象的属性相加,并返回相加后的 Box 对象。
public static Box operator+ (Box b, Box c)
{
Box box = new Box();
box.length = b.length + c.length;
box.breadth = b.breadth + c.breadth;
box.height = b.height + c.height;
return box;
}
注意:运算符只能采用值参数,不能采用 ref 或 out 参数。
1.接口定义了属性、方法和事件,这些都是接口的成员。
2.接口只包含了成员的声明。
3.通常接口命令以 I 字母开头。
4.接口方法不能用public abstract等修饰。
5.必须实现接口的所有方法。
下面是一个高频率的笔试题:
1.抽象类是类,所以只能被单继承,但是接口却可以一次实现多个
2.接口中只能声明方法,属性,事件,索引器。而抽象类中可以有方法的实现,也可以定义非静态的类变量。
3.抽象类的实例是它的子类给出的。接口的实例是实现接口的类给出的。
4.接口成员是公共的,但抽象类的成员也可以是任意的。
作用:一个命名空间中声明的类的名称与另一个命名空间中声明的相同的类的名称不冲突。
定义:
//命名空间1
namespace MyNamespace1
{
// 代码声明
class MyClass()
{
public MyFun1()
{
}
}
}
//命名空间2
namespace MyNamespace2
{
// 代码声明
class MyClass()
{
public MyFun2()
{
}
}
}
使用:
方法一:
MyNamespace1.MyClass class1=new MyNamespace1.MyClass();
MyNamespace2.MyClass class2=new MyNamespace2.MyClass();
class1.MyFun1();
class2.MyFun2();
方法二:使用using关键字
using 关键字表明程序使用的是给定命名空间中的名称
例如:
using MyNamespace1;
MyClass myFun=new MyClass ();
MyClass .MyFun1();
总结using的用法:
using System;
using Namespace1.SubNameSpace;
using static System.Math;var = PI; // 直接使用System.Math.PI
using Project = PC.MyCompany.Project;
using (Font font3 = new Font("Arial", 10.0f),
font4 = new Font("Arial", 10.0f))
{
// Use font3 and font4.
}
//代码段结束时,自动调用font3和font4的Dispose方法
预处理器指令的作用:由于预处理器指令可以禁止编译器编译代码的某一部分,所以计划发布两个版本的代码,即基本版本和有更多功能的企业版本,就可以使用这些预处理器指令来控制。
1.创建符号常量,使用方法如下:
#define PI
#if (PI)
Console.WriteLine("PI is defined");
#else
Console.WriteLine("PI is not defined");
正则表达式是一种匹配输入文本的模式。模式由一个或多个字符、运算符和结构组成。
正则表达式可以用来检查一个串是否含有某种子串、将匹配的子串替换或者从某个串中取出符合某个条件的子串等。
正则表达式中的反斜杠字符(\)指示其后跟的字符是特殊字符。
正则表达式的限定符:
正则表达式的定位符:
注意:不能将限定符与定位符一起使用。
关于正则表达式的学习,请参考:http://www.runoob.com/regexp/regexp-tutorial.html
C# 异常处理是建立在四个关键词之上的:try、catch、finally 和 throw。
格式:
try
{
// 引起异常的语句
}
catch( ExceptionName e1 )
{
// 错误处理代码
}
catch( ExceptionName e2 )
{
// 错误处理代码
}
catch( ExceptionName eN )
{
// 错误处理代码
}
finally
{
// 要执行的语句
}
Catch(Exception e)
{
...
Throw e
}
C# 中的异常类主要是直接或间接地派生于 System.Exception 类。System.ApplicationException 和 System.SystemException 类是派生于 System.Exception 类的异常类。
当打开文件进行读写时,它变成一个 流。流包括:输入流 和 输出流。输入流用于从文件读取数据(读操作),输出流用于向文件写入数据(写操作)。
System.IO 命名空间有各种不同的类,用于执行各种文件操作,如创建和删除文件、读取或写入文件,关闭文件等。