目录
一、概念:.net与c#
net/dotnet:一般指.Net Framework框架.一种平台,一种技术.
C#(sharp):一种编程语言,可以开发基于.net平台的应用
二、.NET两种交互模式
•C/S:客户机(Client)/服务器模式(Server) 如:qq
(C/S是Client/Server的缩写。客户端需要安装专用的客户端软件。)
•B/S:浏览器(Browser)/服务器模式(Server)
(B/S是Brower/Server的缩写,客户机上只要安装一个浏览器)
三、IDE介绍
•IDE(Integrated Development,集成开发环境)
•Visual Studio 2010 简介:
–一套完整的开发工具集
–用于开发桌面应用程序、ASP.NET Web 应用程序、XML、Web Service、移动应用程序
–为快速开发提供强大支持(.net的IDE就是vs2010、vs2012、vs2013)
四、vs文件解读
•.Sln:解决方案文件
•.csproj:项目文件
•.cs:类文件
五、VS中的常用快捷键
•Ctrl+K+D:快速对齐代码
•Ctrl+Z:撤销
•Ctrl+S:保存(一定要经常保存!)
•Ctrl+J:快速弹出智能提示
•Shift+End 、Shift+Home
•Ctrl+K+C:注释所选代码
•Ctrl+K+U:取消对所选代码的注释
•F1:转到帮助文档
•Alt+F10:快速添加命名空间
•折叠冗余代码:#Region 和#EndRegion
•双击tab键补全代码结构
大小写转换
转小写 组合键:Ctrl+ U
转大写 组合键:CTRL + SHIFT + U
删除一行
(1) Ctrl +L
(2) Ctrl +shift +L
(3) Shift +上下键+Backspace
跳转到指定的某一行
组合键:Ctrl+G
矩形区域选择
组合键:Shift+Alt+鼠标拖动 (很适合区域代码块选择)
VS2017 C#字段自动生成属性的快捷键:
https://jingyan.baidu.com/article/fdffd1f8653885f3e98ca13a.html
vs2017快捷键补充:https://www.cnblogs.com/lsgxeva/p/7944986.html
六、变量的命名
•命名规则:
–1 必须以“字母”_或@符号开头.--不要以数字开头
–2 后面可以跟任意“字母”、数字、下划线.
–注意:
•1)你起的变量名不要与c#系统中的关键字重复.
•2)在c#中,大小写是敏感的.
•3)同一个变量名不允许重复定义(先这么认为,不严谨)
•定义变量时,变量名要有意义
•C# 变量命名编码规范——Camel 命名法:
–首个单词的首字母小写,其余单词的首字母大写。
•Pascal命名命名规范:每一个单词第一字母都大写
•如果使用到英文单词的缩写,全部用大写!
标识符命名规则:
标识符是用来识别类、变量、函数或任何其它用户定义的项目。在 C# 中,类的命名必须遵循如下基本规则:
标识符必须以字母、下划线或 @ 开头,后面可以跟一系列的字母、数字( 0 - 9 )、下划线( _ )、@。
标识符中的第一个字符不能是数字。
标识符必须不包含任何嵌入的空格或符号,比如 ? - +! # % ^ & * ( ) [ ] { } . ; : " ' / \。
标识符不能是 C# 关键字。除非它们有一个 @ 前缀。 例如,@if 是有效的标识符,但 if 不是,因为 if 是关键字。
标识符必须区分大小写。大写字母和小写字母被认为是不同的字母。
不能与C#的类库名称相同。
七、算术运算符及算术表达式
•算术运算符: + - * / %(取余数 取模)
•算术表达式:由算术运算符连接起来的式子.如:1+1 a-b(变量ab前面已声明并赋初值)
•演示:某学生三门课成绩为,语文:90 数学:80 英语:67,编程求总分和平均分.
•优先级:先乘除,后加减,有括号先算括号里的,相同级别的从左至右运算 int a=((1+5)+3)*2
•小括号可以无限制的套用,但一定要成对出现.
八、部分数据类型
所有的类型都能够转换成string类型,调用ToString()。
1、枚举
语法:
[public] enum 枚举名
{
值1,
值2,
值3,
........
}
public:访问修饰符。公开的公共的,哪都可以访问。
enum:关键字,声明枚举的关键字
枚举名:要符合Pascal命名规范
我们可以将一个枚举类型的变量跟int类型和string类型互相转换。
枚举类型默认是跟int类型相互兼容的,所以可以通过强制类型转换的语法互相转换。
当转换一个枚举中没有的值的时候,不会抛异常,而是直接将数字显示出来。
枚举同样也可以跟string类型互相转换,如果将枚举类型转换成string类型,则直接调用ToString().
如果将字符串转换成枚举类型则需要下面这样一行代码:
(要转换的枚举类型)Enum.Parse(typeof(要转换的枚举类型),"要转换的字符串");
如果转换的字符串是数字,则就算枚举中没有,也会不会抛异常。
如果转换的字符串是文本,如果枚举中没有,则会抛出异常。
2、常量
声明的常量的语法:
const 变量类型 变量名=值;
3、结构
可以帮助我们一次性声明多个不同类型的变量。
语法:
[public] struct 结构名
{
成员;//字段
}
变量在程序运行期间只能存储一个值,而字段可以存储多个值。
4、数组
一次性存储多个相同类型的变量。
语法:
数组类型[] 数组名=new 数组类型[数组长度];
5、字符串
1)、字符串的不可变性
当你给一个字符串重新赋值之后,老值并没有销毁,而是重新开辟一块空间存储新值。
当程序结束后,GC扫描整个内存,如果发现有的空间没有被指向,则立即把它销毁。
2)、我们可以讲字符串看做是char类型的一个只读数组。
ToCharArray();将字符串转换为char数组
new string(char[] chs):能够将char数组转换为字符串
字符串提供的各种方法
1)、Length:获得当前字符串中字符的个数
2)、ToUpper():将字符转换成大写形式
3)、ToLower():将字符串转换成小写形式
4)、Equals(lessonTwo,StringComparison.OrdinalIgnoreCase):比较两个字符串,可以忽略大小写
5)、Split():分割字符串,返回字符串类型的数组。
6)、Substring():解决字符串。在截取的时候包含要截取的那个位置。(配合charindex解决复杂字符串)
7)、IndexOf():判断某个字符串在字符串中第一次出现的位置,如果没有返回-1、值类型和引用类型在内存上存储的地方不一样。
8)、LastIndexOf():判断某个字符串在字符串中最后一次出现的位置,如果没有同样返回-1
9)、StartsWith():判断以....开始
10)、EndsWith():判断以...结束
11)、Replace():将字符串中某个字符串替换成一个新的字符串
12)、Contains():判断某个字符串是否包含指定的字符串
13)、Trim():去掉字符串中前后的空格
14)、TrimEnd():去掉字符串中结尾的空格
15)、TrimStart():去掉字符串中前面的空格
16)、string.IsNullOrEmpty():判断一个字符串是否为空或者为null
17)、string.Join():将数组按照指定的字符串连接,返回一个字符串。
6、集合
1)、ArrayList集合
Add('...');//添加值
AddRange('...');//添加数据值
Count//集合个数属性
Capacity//集合可包含元素的个数
注:每次集合中实际包含的元素个数(count)超过了可以包含的元素的个数(capcity)的时候,
集合就会向内存中申请多开辟一倍的空间,来保证集合的长度一直够用。
长度可以改变,类型任意。
//创建了一个集合对象
ArrayList list = new ArrayList();
//集合:很多数据的一个集合
//数组:长度不可变、类型单一
//集合的好处:长度可以任意改变 类型随便(函数也可)
list.Add(1);
list.Add(3.14);
list.Add(true);
list.Add("张三");
list.Add('男');
list.Add(5000m);
list.Add(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 });
2)、HashTable键值对集合
对象.Add('值');//添加值
对象.Contains('键');对象.ContainsKey(‘键’);//相同,表现如果有(‘键’)返回true,没有(‘键’)则返回false
对象.ContainsValue('值');同上
对象.Remove('键');//移除指定键的元素
集合.Keys//键
集合.Valuer//值
注:在键值对集合当中,我们是根据键去找值的。
键值对对象[键]=值;
键值对集合当中,键必须是唯一的,而值是可以重复的
//创建了一个键值对集合对象
Hashtable ht = new Hashtable();
ht.Add(1, "张三");
ht.Add(2, true);
ht.Add(3, '男');
ht.Add(false, "错误的");
ht.Add(5, "张三");
ht[6] = "新来的";//这也是一种添加数据的方式
ht[1] = "把张三干掉";
ht.Add("abc", "cba");
3)、字典集合
Dictionary<int, string> dic = new Dictionary<int, string>();
dic.Add(1, "张三");
dic.Add(2, "李四");
dic.Add(3, "王五");//新增
dic[1] = "新来的";//更新
foreach (KeyValuePair<int,string> kv in dic)//遍历
{
Console.WriteLine("{0}---{1}",kv.Key,kv.Value);
}
//foreach (var item in dic.Keys)//遍历;var:根据值可以推断出变量类型;
//{
// Console.WriteLine("{0}---{1}",item,dic[item]);
//}
Hashtable和Dictionary都是.Net下的表示键值对的集合,那么我们在使用中该选择Hashtable还是Dictionary?下边我们看看他们之间的区别:
1、Dictionary<K,V>在使用中是顺序存储的,而Hashtable由于使用的是哈希算法进行数据存储,是无序的。
2、Dictionary的key和value是泛型存储,Hashtable的key和value都是object
3、Dictionary是泛型存储,不需要进行类型转换,Hashtable由于使用object,在存储或者读取值时都需要进行类型转换,所以比较耗时
4、单线程程序中推荐使用 Dictionary, 有泛型优势, 且读取速度较快, 容量利用更充分。多线程程序中推荐使用 Hashtable, 默认的 Hashtable 允许单线程写入, 多线程读取, 对 Hashtable 进一步调用 Synchronized() 方法可以获得完全线程安全的类型. 而 Dictionary 非线程安全, 必须人为使用 lock 语句进行保护, 效率大减。
5、在通过代码测试的时候发现key是整数型Dictionary的效率比Hashtable快,如果key是字符串型,Dictionary的效率没有Hashtable快。
对于如何进行选择,个人倾向于使用Dictionary,原因是:
1、Dictionary是可排序的,Hashtable如果想排序还需要采用别的方式进行
2、Dictionary有泛型优势,效率要高
7、list泛型
泛型(Generic) 允许您延迟编写类或方法中的编程元素的数据类型的规范,直到实际在程序中使用它的时候。换句话说,泛型允许您编写一个可以与任何数据类型一起工作的类或方法。
泛型(Generic)的特性
使用泛型是一种增强程序功能的技术,具体表现在以下几个方面:
- 它有助于您最大限度地重用代码、保护类型的安全以及提高性能。
- 您可以创建泛型集合类。.NET 框架类库在 System.Collections.Generic 命名空间中包含了一些新的泛型集合类。您可以使用这些泛型集合类来替代 System.Collections 中的集合类。
- 您可以创建自己的泛型接口、泛型类、泛型方法、泛型事件和泛型委托。
- 您可以对泛型类进行约束以访问特定数据类型的方法。
- 关于泛型数据类型中使用的类型的信息可在运行时通过使用反射获取。
转载:https://www.runoob.com/csharp/csharp-generic.html
部分函数实例:
创建泛型集合对象
List<int> list = new List<int>();
list.Add(1);//添加单个元素
list.AddRange(new int[] { 1, 2, 3, 4, 5, 6 });//添加多个元素
list.AddRange(list);
//List泛型集合可以转换为数组
int[] nums = list.ToArray();
装箱、拆箱
装箱:就是将值类型转换为引用类型。
拆箱:将引用类型转换为值类型。
看两种类型是否发生了装箱或者拆箱,要看,这两种类型是否存在继承关系。
int n = 10;
object o = n;//装箱
int nn = (int)o;//拆箱
8、类型转换
在c#中类型的转换分两种:显式和隐式,基本的规则如下:
1、基类对象转化为子类对象,必须显式转换,规则:(类型名) 对象。
2、值类型和引用类型的转换采用装箱(boxing)或拆箱(unboxing).
3、子类转化为基类对象。
4、基本类型互相之间转化可以用Covent类来实现。
5、字符串类型转换为对应的基本类型用Parse方法,除了String类型外其他的类型都可以用Parse方法。
6、用GetType可以取得对象的精确类型。
7、子类转化为基类,采用隐式转换。
在进行类型转换的时候,可以按照如下的方式进行选择
1、Object => 已知引用类型
使用as操作符来完成
2、Object => 已知值类型
先使用is操作符来进行判断,再用类型强转方式进行转换
3、已知引用类型之间转换
首先需要相应类型提供转换函数,再用类型强转方式进行转换
4、已知值类型之间转换
最好使用系统提供的Convert类所涉及的静态方法
(int)和Int32.Parse(),Convert.ToInt32()三者的区别
1、(int)转换:用在数值范围大的类型转换成数值范围小的类型时使用,但是如果被转换的数值大于或者小于数值范围,则得到一个错误的结果,利用这种转换方式不能将string转换成int,会报错。
2、Int32.Parse()转换:在符合数字格式的string到int类型转换过程中使用,并可以对错误的string数字格式的抛出相应的异常。
3、Convert.ToInt32()转换:使用这种转换,所提供的字符串必须是数值的有效表达方式,该数还必须不是溢出的数。否则抛出异常。
希望本文所述对大家的C#程序设计有所帮助。
九、类
1、继承
继承的特性
1)、继承的单根性:一个子类只能有一个父类。
2)、继承的传递性
子类对象可以调用父类中的成员,但是父类对象永远都只能调用自己的成员。
1)子类继承了父类,那么子类从父类那里继承过来了什么?
首先,子类继承了父类的属性和方法,但是子类并没有继承父类的私有字段。
问题:子类有没有继承父类的构造函数?
答:子类并没有继承父类的构造函数,但是。子类会默认的调用父类无参数的构造函数,
2)创建父类对象,让子类可以使用父类中的成员。
所以,如果在父类中重新写了一个有参数的构造函数之后,那个无参数的就被干掉了,
子类就调用不到了,所以子类会报错。
解决办法:
在父类中重新写一个无参数的构造函数。
在子类中显示的调用父类的构造函数,使用关键字:base()
里氏转换
1)、子类可以赋值给父类
2)、如果父类中装的是子类对象,那么可以讲这个父类强转为子类对象。
2、多态
首先解释下什么叫多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果,这就是多态性。换句话说,实际上就是同一个类型的实例调用"相同"的方法,产生的结果是不同的。这里的"相同"打上双引号是因为这里的相同的方法仅仅是看上去相同的方法,实际上它们调用的方法是不同的。
说到多态,我们不能免俗的提到下面几个概念:重载、重写、虚方法、抽象方法以及隐藏方法。下面就来一一介绍他们的概念。
1、重载(overload): 在同一个作用域(一般指一个类)的两个或多个方法函数名相同,参数列表不同的方法叫做重载,它们有三个特点(俗称两必须一可以):
- 方法名必须相同
- 参数列表必须不相同
- 返回值类型可以不相同
public void Sleep()
{
Console.WriteLine("Animal睡觉");
}
public int Sleep(int time)
{
Console.WriteLine("Animal{0}点睡觉", time);
return time;
}
2、重写(override):子类中为满足自己的需要来重复定义某个方法的不同实现,需要用 override 关键字,被重写的方法必须是虚方法,用的是 virtual 关键字。它的特点是(三个相同):
- 相同的方法名
- 相同的参数列表
- 相同的返回值
如:父类中的定义:
public virtual void EatFood()
{
Console.WriteLine("Animal吃东西");
}
子类中的定义:
public override void EatFood()
{
Console.WriteLine("Cat吃东西");
//base.EatFood();
}
小提示:经常有童鞋问重载和重写的区别,而且网络上把这两个的区别作为 C# 做常考的面试题之一。实际上这两个概念完全没有关系,仅仅都带有一个"重"字。他们没有在一起比较的意义,仅仅分辨它们不同的定义就好了。
3、虚方法:即为基类中定义的允许在派生类中重写的方法,使用virtual关键字定义。如:
public virtual void EatFood()
{
Console.WriteLine("Animal吃东西");
}
就是将父类的方法标记为虚方法 ,使用关键字 virtual,这个函数可以被子类重新写一个遍。
继承的子类可以用override 可重写子类方法。
public class Person
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
public Person(string name)
{
this.Name = name;
}
public virtual void SayHello()//虚方法
{
Console.WriteLine("我是人类");
}
}
public class Chinese : Person
{
public Chinese(string name)
: base(name)
{
}
public override void SayHello()//重写
{
Console.WriteLine("我是中国人,我叫{0}", this.Name);
}
}
注意:虚方法也可以被直接调用。如:
Animal a = new Animal();
a.EatFood();
执行输出结果为:
Animal吃东西
4、抽象方法:在基类中定义的并且必须在派生类中重写的方法,使用 abstract 关键字定义。如:
public abstract class Biology
{
public abstract void Live();
}
public class Animal : Biology
{
public override void Live()
{
Console.WriteLine("Animal重写的抽象方法");
//throw new NotImplementedException();
}
}
注意:抽象方法只能在抽象类中定义,如果不在抽象类中定义,则会报出如下错误:
虚方法和抽象方法的区别是:因为抽象类无法实例化,所以抽象方法没有办法被调用,也就是说抽象方法永远不可能被实现。
5、隐藏方法:在派生类中定义的和基类中的某个方法同名的方法,使用 new 关键字定义。如在基类 Animal 中有一方法 Sleep():
public void Sleep()
{
Console.WriteLine("Animal Sleep");
}
则在派生类 Cat 中定义隐藏方法的代码为:
new public void Sleep()
{
Console.WriteLine("Cat Sleep");
}
或者为:
public new void Sleep()
{
Console.WriteLine("Cat Sleep");
}
注意:
- (1)隐藏方法不但可以隐藏基类中的虚方法,而且也可以隐藏基类中的非虚方法。
- (2)隐藏方法中父类的实例调用父类的方法,子类的实例调用子类的方法。
- (3)和上一条对比:重写方法中子类的变量调用子类重写的方法,父类的变量要看这个父类引用的是子类的实例还是本身的实例,如果引用的是父类的实例那么调用基类的方法,如果引用的是派生类的实例则调用派生类的方法。
(以上)转载自:https://www.runoob.com/w3cnote/csharp-polymorphism2.html
1)、partial部分类
使用partial关键字可以声明部分类, 部分类的作用是可以在多个文件中声明同一个类, 主要用于类比较大的时候进行拆分,或者xaml中使用也比较多。
public partial class Person
{
private string _name;
public void Test()
{
}
}
public partial class Person
{
public void Test(string name)
{
}
}
不要把部分类以为是定义了多个类,其实还是一个类,只是把这个类拆分了。 在程序运行的时候编译器会把这个类合并在一起的, 这样做的好处是,当你有一个类很大的时候你可以按实现功能拆分在不同的文件中,这样就方便阅读和修改了。
2)、sealed密封类
不能够被其他类继承,但是可以继承于其他类。
密封类可以用来限制扩展性,如果密封了某个类,则其他类不能从该类继承;如果密封了某个成员,则派生类不能重写该成员的实现。默认情况下,不应密封类型和成员。密封可以防止对库的类型和成员进行自定义,但也会影响某些开发人员对可用性的认识。
C#中使用密封类时,如果类满足如下条件,则应将其密封。
类是静态类。
类包含带有安全敏感信息的继承的受保护成员。
类继承多个虚成员,并且密封每个成员的开发和测试开销明显大于密封整个类。
类是一个要求使用反射进行快速搜索的属性。密封属性可提高反射在检索属性时的性能。
访问修饰符 sealed class 类名:
基类或接口
{
//类成员
}
- 说明:① 密封类不能作为基类被继承,但它可以继承别的类或接口。
- ② 在密封类中不能声明受保护成员或虚成员,因为受保护成员只能从派生类进行访问,而虚成员只能在派生类中重写。
- ③ 由于密封类的不可继承性,因此密封类不能声明为抽象的,即sealed修饰符不能与abstract修饰符同时使用。
转载:https://www.cnblogs.com/bdqnquliang/p/6664294.html
3)接口
接口定义了所有类继承接口时应遵循的语法合同。接口定义了语法合同 "是什么" 部分,派生类定义了语法合同 "怎么做" 部分。
接口定义了属性、方法和事件,这些都是接口的成员。接口只包含了成员的声明。成员的定义是派生类的责任。接口提供了派生类应遵循的标准结构。
接口使得实现接口的类或结构在形式上保持一致。
抽象类在某种程度上与接口类似,但是,它们大多只是用在当只有少数方法由基类声明由派生类实现时。
[public] interface I..able
{
成员;
}
接口的实现与类的继承语法格式类似:
class InterfaceImplementer : IMyInterface
以下实例定义了两个接口 IMyInterface 和 IParentInterface。
如果一个接口继承其他接口,那么实现类或结构就需要实现所有接口的成员。
以下实例 IMyInterface 继承了 IParentInterface 接口,因此接口实现类必须实现 MethodToImplement() 和 ParentInterfaceMethod() 方法:
using System;
interface IParentInterface
{
void ParentInterfaceMethod();
}
interface IMyInterface : IParentInterface
{
void MethodToImplement();
}
class InterfaceImplementer : IMyInterface
{
static void Main()
{
InterfaceImplementer iImp = new InterfaceImplementer();
iImp.MethodToImplement();
iImp.ParentInterfaceMethod();
}
public void MethodToImplement()
{
Console.WriteLine("MethodToImplement() called.");
}
public void ParentInterfaceMethod()
{
Console.WriteLine("ParentInterfaceMethod() called.");
}
}
实例输出结果为:
MethodToImplement() called.
ParentInterfaceMethod() called.
注意:
- 接口方法不能用public abstract等修饰。接口内不能有字段变量,构造函数。(默认是public)
- 接口内可以定义属性(有get和set的方法)。如string color { get ; set ; }这种。
- 实现接口时,必须和接口的格式一致。
- 必须实现接口的所有方法。
接口语法解读:
- 接口是一种规范;
- 只要一个类继承了一个接口,这个类就必须实现这个接口中所有的成员;
- 为了多态。 接口不能被实例化;
- 也就是说,接口不能new(不能创建对象)
- 接口中的成员不能加“访问修饰符”,接口中的成员访问修饰符为public,不能修改。
- (接口里默认为public) 接口中的成员不能有任何实现(“光说不做”,只是定义了一组未实现的成员)。(类中默认为private )
- 接口中只能有方法、属性、索引器、事件,不能有“字段”和构造函数。
- 接口与接口之间可以继承,并且可以多继承。
- 接口并不能去继承一个类,而类可以继承接口 (接口只能继承于接口,而类既可以继承接口,也可以继承类)
- 实现接口的子类必须实现该接口的全部成员。
- 一个类可以同时继承一个类并实现多个接口,如果一个子类同时继承了父类A,并实现了接口IA,那么语法上A必须写在IA的前面。
- class MyClass:A,IA{},因为类是单继承的。
显示实现接口的目的:解决方法的重名问题
什么时候显示的去实现接口:
当继承的借口中的方法和参数一摸一样的时候,要是用显示的实现接口
class Program
{
static void Main(string[] args)
{
//显示实现接口就是为了解决方法的重名问题
IFlyable fly = new Bird();
fly.Fly();
Bird bird = new Bird();
bird.Fly();
Console.ReadKey();
}
}
public class Bird : IFlyable
{
public void Fly()
{
Console.WriteLine("鸟飞会");
}
/// <summary>
/// 显示实现接口
/// </summary>
void IFlyable.Fly()
{
Console.WriteLine("我是接口的飞");
}
部分转载:https://www.runoob.com/csharp/csharp-interface.html
十、命名空间
可以认为类是属于命名空间的。
如果在当前项目中没有这个类的命名空间,需要我们手动的导入这个类所在的
命名空间。
1)、用鼠标去点
2)、alt+shift+F10
3)、记住命名空间,手动的去引用
在一个项目中引用另一个项目的类
1)、添加引用(选中项目——>右键——>添加引用)
2)、引用命名空间(例:using 类的namespace)
十一、部分实用函数使用
1、计算程序运行时间
Stopwatch sw = new Stopwatch();
sw.Start();//开始
foreach (var item in nums)
{
}
sw.Stop();//结束
Console.WriteLine(sw.Elapsed);//输出运行时间
2、重写父类的ToStrong()
public class Person
{
public override string ToString()
{
return "Hello World";
}
}
3、foreach循环
例:
foreach (var item in collection)//var:根据值可以推断出变量类型;item集合每一个元素;in在...里;collection集合
{
}
foreach (var item in hast.Keys)
{
Console.WriteLine("{0},{1}", hast[item], item);
}
Console.Read();
十二、常用关键字解析
最全关键字详情:https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/
关键字是 C# 编译器预定义的保留字。这些关键字不能用作标识符,但是,如果您想使用这些关键字作为标识符,可以在关键字前面加上 @ 字符作为前缀。
在 C# 中,有些关键字在代码的上下文中有特殊的意义,如 get 和 set,这些被称为上下文关键字(contextual keywords)。
下表列出了 C# 中的保留关键字(Reserved Keywords)和上下文关键字(Contextual Keywords):
保留关键字 | ||||||
abstract | as | base | bool | break | byte | case |
catch | char | checked | class | const | continue | decimal |
default | delegate | do | double | else | enum | event |
explicit | extern | false | finally | fixed | float | for |
foreach | goto | if | implicit | in | in (generic modifier) | int |
interface | internal | is | lock | long | namespace | new |
interface | internal | is | lock | long | namespace | new |
private | protected | public | readonly | ref | return | sbyte |
sealed | short | sizeof | stackalloc | static | string | struct |
switch | this | throw | true | try | typeof | uint |
ulong | unchecked | unsafe | ushort | using | virtual | void |
volatile | while | |||||
上下文关键字 | ||||||
add | alias | ascending | descending | dynamic | from | get |
global | group | into | join | let | orderby | partial (type) |
partial (method) | remove | select | set |
abstract | as | base | bool | break | byte | case |
catch | char | checked | class | const | continue | decimal |
default | delegate | do | double | else | enum | event |
explicit | extern | false | finally | fixed | float | for |
foreach | goto | if | implicit | in | in (generic modifier) | int |
interface | internal | is | lock | long | namespace | new |
interface | internal | is | lock | long | namespace | new |
private | protected | public | readonly | ref | return | sbyte |
sealed | short | sizeof | stackalloc | static | string | struct |
switch | this | throw | true | try | typeof | uint |
ulong | unchecked | unsafe | ushort | using | virtual | void |
volatile | while | |||||
add | alias | ascending | descending | dynamic | from | get |
global | group | into | join | let | orderby | partial (type) |
部分转载:https://www.cnblogs.com/ECJTUACM-873284962/p/7635991.html
1、is、as
C#中的is
检查一个对象是否兼容于其他指定的类型,并返回一个Bool值,如果一个对象是某个类型或是其父类型的话就返回为true,否则的话就会返回为false。永远不会抛出异常
如果对象引用为null,那么is操作符总是返回为false,因为没有对象可以检查其类型。
例如
代码如下:
object o = new object();
if (o is Label)
{
Label lb = (Label)o;
Response.Write("类型转换成功");
}
else
{
Response.Write("类型转换失败");
}
C#中as的转换规则
1、检查对象类型的兼容性,并返回转换结果,如果不兼容则返回null;
2、不会抛出异常;
3、如果结果判断为空,则强制执行类型转换将抛出NullReferenceException异常;
4、用as来进行类型转换的时候,所要转换的对象类型必须是目标类型或者转换目标类型的派生类型
例如
代码如下:
object o = new object();
Label lb = o as Label;
if (lb == null)
{
Response.Write("类型转换失败");
}
else
{
Response.Write("类型转换成功");
}
使用as操作符有如下几点限制
第一个就是,不用在类型之间进行类型转化,即如下编写就会出现编译错误。
代码如下:
NewType newValue = new NewType();
NewType1 newValue = newValue as NewType1;
第二个就是,不能应用在值类型数据,即不能如下写(也会出现编译错误)。
代码如下:
object objTest = 11;
int nValue = objTest as int;
as与is的区别
1、AS在转换的同事兼判断兼容性,如果无法进行转换,则 as 返回 null(没有产生新的对象)而不是引发异常。有了AS我想以后就不要再用try-catch来做类型转换的判断了。因此as转换成功要判断是否为null。
2、AS是引用类型类型的转换或者装箱转换,不能用与值类型的转换。如果是值类型只能结合is来强制转换
3、IS只是做类型兼容判断,并不执行真正的类型转换。返回true或false,不会返回null,对象为null也会返回false。
4、AS模式的效率要比IS模式的高,因为借助IS进行类型转换的化,需要执行两次类型兼容检查。而AS只需要做一次类型兼容,一次null检查,null检查要比类型兼容检查快。
2、base和this
base:
用于在派生类中实现对基类公有或者受保护成员的访问,但是只局限在构造函数、实例方法和实例属性访问器中。
MSDN中小结的具体功能包括:
(1)调用基类上已被其他方法重写的方法。
(2)指定创建派生类实例时应调用的基类构造函数。
base常用于,在派生类对象初始化时和基类进行通信。
base可以访问基类的公有成员和受保护成员,私有成员是不可访问的。
在多层继承中,base可以指向的父类的方法有两种情况:一是有重载存在的情况下,base将指向直接继承的父类成员的方法;而没有重载存在的情况下,base可以指向任何上级父类的公有或者受保护方法。
this:
1)、代表当前类的对象
2)、在类当中显示的调用本类的构造函数 :this
用于引用类的当前实例,也包括继承而来的方法,通常可以隐藏this。
MSDN中的小结功能主要包括:
(1)限定被相似的名称隐藏的成员
(2)将对象作为参数传递到其他方法
(3)声明索引器
this指代类对象本身,用于访问本类的所有常量、字段、属性和方法成员,而且不管访问元素是任何访问级别。因为,this仅仅局限于对象内部,对象外部是无法看到的,这就是this的基本思想。另外,静态成员不是对象的一部分,因此不能在静态方法中引用this。
通用规则:
1、尽量少用或者不用base和this。除了避开子类的名称冲突和在一个构造函数中调用其他的构造函数之外,base和this的使用容易引起不必要的结果。
2、在静态成员中使用base和this都是不允许的。原因是,base和this访问的都是类的实例,也就是对象,而静态成员只能由类来访问,不能由对象来访问。
3、base是为了实现多态而设计的。
4、使用this或base关键字只能指定一个构造函数,也就是说不可同时将this和base作用在一个构造函数上。
5、简单的来说,base用于在派生类中访问重写的基类成员;而this用于访问本类的成员,当然也包括继承而来公有和保护成员。
6、除了base,访问基类成员的另外一种方式是:显示的类型转换来实现。只是该方法不能为静态方法。
base调用构造方法
public class BaseClass
{
public BaseClass()
{
Console.WriteLine("调用基类--无参数的构造函数");
}
public BaseClass(string name)
{
Console.WriteLine("调用基类--有参数的构造函数");
}
}
public class DerivedClass:BaseClass
{
public DerivedClass()
: base()
{
}
public DerivedClass(string name)
: base(name)
{
}
static void Main()
{
DerivedClass a = new DerivedClass();//调用基类--无参数的构造函数
DerivedClass b = new DerivedClass("Andy");//调用基类--有参数的构造函数
Console.ReadLine();
}
}
base在派生类中调用基类的方法
public class BaseClass
{
public virtual void GetInfo()
{
Console.WriteLine("Andy.");
}
}
public class DerivedClass :BaseClass
{
public override void GetInfo()
{
base.GetInfo();//调用基类的方法,显示Andy.
Console.WriteLine("Chen");//显示Andy.Chen
}
}
base和this的综合使用
public class MyBaseClass
{
public MyBaseClass()
{
Console.Write("调用父类无参数的构造函数");
}
public MyBaseClass(int i)
{
Console.Write("调用父类一个参数的构造函数");
}
}
public class MyDerivedClass : MyBaseClass
{
public int age;
public static int age2;//只要类里存在静态变量,那么静态变量总是最先初始化的。
//静态构造函数只执行一次
static MyDerivedClass() //既然要初始化静态变量,就要调用静态的构造函数。
{
age2 = 100;
Console.Write(age2);
}
public MyDerivedClass()
: this(5)//调用当前实例有参数的构造函数。如果只调用这个构造函数,那还需要调用一次基类没有参的构造函数!!!
{
age = 101;
Console.WriteLine(age);
}
public MyDerivedClass(int i) : base(i)//调用基类有参数的构造函数
{
age = 102;
Console.WriteLine(age);
}
public MyDerivedClass(int i, int j)
{
Console.WriteLine("两个变量的参数");
}
}
class Program
{
static void Main(string[] args)
{
MyDerivedClass myder = new MyDerivedClass(); //输出100 ,"调用父类无参数的构造函数",101
//执行顺序:<1>: static MyDerivedClass() <2>: public MyBaseClass() <3>:public MyDerivedClass()
//---------------------------------------------------------------------------------------------
MyDerivedClass myder2 = new MyDerivedClass(5); //输出"调用父类无参数的构造函数",102。
//在初始化myder对象的时候已经初始化过静态变量age2了。因为静态构造函数最多执行一次,所以初始化myder2对象的时候就不会在继续初始化静态变量age2了
//执行顺序:<1>: public MyBaseClass() <2>: public MyDerivedClass(int i)
//假如我想在初始化myder2对象的时候调用父类带有一个参数的构造函数怎么办呢?很好办只要在派生类的构造函数后面加一个:base(i)
/* base 关键字用于从派生类中访问基类的成员;指定创建派生类实例时应调用的基类构造函数。
public MyDerivedClass(int i):base(i)
{
age = 102;
Console.WriteLine(age);
}
执行顺序:<1>: public MyBaseClass(int i) <2>: public MyDerivedClass(int i)
顾这里输出的是 "调用父类一个参数的构造函数",102
*/
//---------------------------------------------------------------------------------------------
MyDerivedClass myder3 = new MyDerivedClass(5, 6); //输出"调用父类无参数的构造函数","两个变量的参数"
//执行顺序:<1>: public MyBaseClass() <2>: public MyDerivedClass(int i, int j)
Console.ReadKey();
}
}
3、using
using 关键字有两个主要用途:
(一).作为指令,用于为命名空间创建别名或导入其他命名空间中定义的类型。
(二).作为语句,用于定义一个范围,在此范围的末尾将释放对象。
1)using指令
①允许在命名空间中使用类型,这样,您就不必在该命名空间中限定某个类型的使用:
using System.Text;
using PC.Company;
②为命名空间或类型创建别名。
using MyCompany = PC.Company; //命名空间的别名。
using Project = PC.Company.Project; //类型的别名
using引入命名空间,并不等于编译器编译时加载该命名空间所在的程序集,程序集的加载决定于程序中对该程序集是否存在调用操作,如果代码中不存在任何调用操作则编译器将不会加载using引入命名空间所在程序集。因此,在源文件开头,引入多个命名空间,并非加载多个程序集,不会造成“过度引用”的弊端。
创建别名的另一个重要的原因在于同一文件中引入的不同命名空间中包括了相同名称的类型,如SharpMap.Geometries.Point与System.Drawing.Point。为了避免出现名称冲突,可以通过设定别名来解决:
using SGPoint = SharpMap.Geometries.Point;
using SDPoint = System.Drawing.Point;
尽管我们可以通过类型全名称来加以区分,但是这显然不是最佳的解决方案。用using指令创建别名,有效的解决了这种可能的命名冲突,才是最佳的解决方案。
2)using语句
using 语句允许程序员指定使用资源的对象应当何时释放资源。using 语句中使用的对象必须实现 IDisposable 接口。此接口提供了 Dispose 方法,该方法将释放此对象的资源。
①可以在 using 语句之中声明对象。
Font font2 = new Font("Arial", 10.0f);
using (font2)
{
// use font2
}
②可以在 using 语句之前声明对象。
using (Font font2 = new Font("Arial", 10.0f))
{
// use font2
}
③可以有多个对象与 using 语句一起使用,但是必须在 using 语句内部声明这些对象。
using (Font font3=new Font("Arial",10.0f), font4=new Font("Arial",10.0f))
{
// Use font3 and font4.
}
使用规则
①using只能用于实现了IDisposable接口的类型,禁止为不支持IDisposable接口的类型使用using语句,否则会出现编译错误;
②using语句适用于清理单个非托管资源的情况,而多个非托管对象的清理最好以try-finnaly来实现,因为嵌套的using语句可能存在隐藏的Bug。内层using块引发异常时,将不能释放外层using块的对象资源;
③using语句支持初始化多个变量,但前提是这些变量的类型必须相同,例如:
using(Pen p1 = new Pen(Brushes.Black), p2 = new Pen(Brushes.Blue))
{
//
}
④针对初始化对个不同类型的变量时,可以都声明为IDisposable类型,例如:
using (IDisposable font = new Font("Verdana", 12), pen = new Pen(Brushes.Black))
{
float size = (font as Font).Size;
Brush brush = (pen as Pen).Brush;
}
using实质
在程序编译阶段,编译器会自动将using语句生成为try-finally语句,并在finally块中调用对象的Dispose方法,来清理资源。所以,using语句等效于try-finally语句,例如:
Font f2 = new Font("Arial", 10, FontStyle.Bold);
try
{
//执行文本绘制操作
}
finally
{
if (f2 != null) ((IDisposable)f2).Dispose();
}
转载:https://blog.youkuaiyun.com/WanderOCN/article/details/6659811
4、new
1)、创建对象
2)、隐藏从父类那里继承过来的成员
public class Person
{
public void T()
{}
}
public class Teacher : Person
{
public new void T()//隐藏从父类那里继承过来的成员
{}
}
十三、文件操作
1、使用FileStream来读写文件
System.IO 命名空间中的 FileStream 类有助于文件的读写与关闭。该类派生自抽象类 Stream。
您需要创建一个 FileStream 对象来创建一个新的文件,或打开一个已有的文件。创建 FileStream 对象的语法如下:
FileStream <object_name> = new FileStream( <file_name>,
<FileMode Enumerator>, <FileAccess Enumerator>, <FileShare Enumerator>);
例如,创建一个 FileStream 对象 F 来读取名为 sample.txt 的文件:
FileStream F = new FileStream("sample.txt", FileMode.Open, FileAccess.Read, FileShare.Read);//参数:路径,打开(无此文件报错),读取,允许随后打开文件读取
读取txt文件整个过程:
读小txt文件:
FileStream fsRead = new FileStream(@"C:\Users\SpringRain\Desktop\new.txt", FileMode.OpenOrCreate, FileAccess.Read);//参数:文件地址,打开文件(没有文件则创建),读取
byte[] buffer = new byte[1024 * 1024 * 5];//字节数组5M
//返回本次实际读取到的有效字节数
int r = fsRead.Read(buffer, 0, buffer.Length);//参数:每次读取文件流的大小,开始位置,最多读取的长度
//将字节数组中每一个元素按照指定的编码格式解码成字符串
string s = Encoding.UTF8.GetString(buffer, 0, r);//参数:字节,第一个要解码字节的索引,要解码的字节数
//关闭流
fsRead.Close();
//释放流所占用的资源
fsRead.Dispose();
读大文件:
注:将创建文件流对象的过程写在using当中,会自动的帮助我们释放流所占用的资源。
txt文件写入:
using (FileStream fsWrite = new FileStream(@"C:\Users\SpringRain\Desktop\new.txt", FileMode.OpenOrCreate, FileAccess.Write))
{
string str = "看我游牧又把你覆盖掉";
byte[] buffer = Encoding.UTF8.GetBytes(str);
fsWrite.Write(buffer, 0, buffer.Length);//参数:字节数组,开始位置,长度
}
Console.WriteLine("写入OK");
Console.ReadKey();
多媒体文件的复制:
static void Main(string[] args)
{
//思路:就是先将要复制的多媒体文件读取出来,然后再写入到你指定的位置
string source = @"C:\Users\SpringRain\Desktop\1、复习.wmv";//读取位置
string target = @"C:\Users\SpringRain\Desktop\new.wmv";//复制目的位置
CopyFile(source, target);
Console.WriteLine("复制成功");
Console.ReadKey();
}
public static void CopyFile(string soucre, string target)
{
//1、我们创建一个负责读取的流
using (FileStream fsRead = new FileStream(soucre, FileMode.Open, FileAccess.Read))
{
//2、创建一个负责写入的流
using (FileStream fsWrite = new FileStream(target, FileMode.OpenOrCreate, FileAccess.Write))
{
byte[] buffer = new byte[1024 * 1024 * 5];
//因为文件可能会比较大,所以我们在读取的时候 应该通过一个循环去读取
while (true)
{
//返回本次读取实际读取到的字节数
int r = fsRead.Read(buffer, 0, buffer.Length);
//如果返回一个0,也就意味什么都没有读取到,读取完了
if (r == 0)
{
break;
}
fsWrite.Write(buffer, 0, r);
}
}
}
StreamReader和StreamWriter:
//使用StreamReader来读取一个文本文件
using (StreamReader sr = new StreamReader(@"C:\Users\SpringRain\Desktop\抽象类特点.txt",Encoding.Default))
{
while (!sr.EndOfStream)
{
Console.WriteLine(sr.ReadLine());//每次读一行
}
}
//使用StreamWriter来写入一个文本文件
using (StreamWriter sw = new StreamWriter(@"C:\Users\SpringRain\Desktop\newnew.txt",true))//true表示追加
{
sw.Write("看我有木有把你覆盖掉");
}
FileStream 操作字节的
StreamReader和StreamWriter 操作字符的(操作文本的)
部分转载:https://www.runoob.com/csharp/csharp-file-io.html
2、path类
①Path类是static类型
②常用方法
Path.GetFullPath(file) 取全路径
Path.GetFileName(file) 取文件名,包含扩展名
Path.GetFileNameWithoutExtension(file) 取文件名,不包含扩展名
Path.GetExtension(file) 取扩展名
Path.GetDirectoryName(file) 取路径名
Path.GetPathRoot(file) 取盘符
Path.Combine(file1,file2) 合并2个路径
③注意事项
a.使用前需要对参数进行判空,其次追加try catch来捕获Exception。
string fullpath7 = null;
if (!String.IsNullOrWhiteSpace(file))
{
try
{
fullpath7 = Path.GetFullPath(file);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
b.GetFullPath()可以将多个 / \ 合并为1个,使之格式化为合法的路径。末尾的\会保留。
c.GetDirectoryName()结果末尾没有\。
d.Combine() 不会合并多个 / \ ,第二个参数不能以/ \ 开头。
e.利用GetDirectoryName()和Combine()去掉末尾\.
string dir2 = Path.GetDirectoryName(Path.Combine(@"D:\1","Temp")); // D:\1
string dir3 = Path.GetDirectoryName(Path.Combine(@"D:\1\", "Temp")); // D:\1
public static void TestPath()
{
string file = "App.config"; //c:\users\qqq\source\repos\StudyDesignMode\StudyDesignMode\bin\Debug\App.config
/* 正常用法 */
//取完整路径
string fullpath = Path.GetFullPath(file); //c:\users\qqq\source\repos\StudyDesignMode\StudyDesignMode\bin\Debug\App.config
//取文件名
string name = Path.GetFileName(@"D:\1\2\App.config"); //App.config
//取扩展名
string extension = Path.GetExtension(@"D:\1\2\App.config"); //.config
//取文件名 不带扩展名
string nameWithoutExtension = Path.GetFileNameWithoutExtension(@"D:\1\2\App.config"); //App
//取所在文件夹
string dir = Path.GetDirectoryName(@"D:\1\2\App.config"); //D:\1\2
//取所在磁盘
string root = Path.GetPathRoot(@"D:\1\2\App.config"); //D:\
//连接路径
string combine = Path.Combine(@"1", @"2"); // 1\2
/* 异常用法 */
//参数为null
//string fullpath1 = Path.GetFullPath(null); //Exception
//参数为空字符串
//string fullpath2 = Path.GetFullPath(""); //Exception
string fullpath3 = Path.GetFullPath(@"D:\\//\\//dfjk\\\\1///2\/\/\/\/\/3\"); //D:\dfjk\1\2\3\ 忽略了多个 / \ 为1个 \ 。保留了末尾的 \ 。
//无扩展名
string name1 = Path.GetFileName(@"D:\1\2\App"); //App
//只有扩展名
string name2 = Path.GetFileName(@"D:\1\2\.config"); //.config
//无文件名
string name3 = Path.GetFileName(@"D:\1\2\"); // ""
//只有盘符
string name4 = Path.GetFileName(@"D:"); // ""
//参数为null
string name5 = Path.GetFileName(null); // null
//参数为""
string name6 = Path.GetFileName(""); // ""
//无扩展名
string extension1 = Path.GetExtension(@"D:\1\2\App"); //""
//只有扩展名
string extension2 = Path.GetExtension(@"D:\1\2\.config"); //.config
//无文件名
string extension3 = Path.GetExtension(@"D:\1\2\"); // ""
//只有盘符
string extension4 = Path.GetExtension(@"D:"); // ""
//参数为null
string extension5 = Path.GetExtension(null); // null
//参数为""
string extension6 = Path.GetExtension(""); // ""
//参数为null
//string combine1 = Path.Combine(null,null); // Exception
//string combine2 = Path.Combine("", null); // Exception
//参数为""
string combine3 = Path.Combine("", ""); // ""
//多个/ \
string combine4 = Path.Combine(@"///1\\\2\3", @"4"); // ///1\\\2\3\4
//第二个参数以/开头
string combine5 = Path.Combine(@"///1\\\2\3", @"/4"); // /4
//第二个参数以\开头
string combine6 = Path.Combine(@"///1\\\2\3", @"\4"); // \4
//第一个参数以\结尾
string combine7 = Path.Combine(@"///1\\\2\3\\", @"4"); // ///1\\\2\3\\4
//第一个参数以/结尾
string combine8 = Path.Combine(@"///1\\\2\3/", @"4"); // ///1\\\2\3/4
//第二个参数以/开头
string combine9 = Path.Combine(@"///1\\\2\3\", @"/4"); // /4
//第二个参数以\开头
string combine10 = Path.Combine(@"///1\\\2\3\", @"\4"); // \4
//取所在文件夹
string dir1 = Path.GetDirectoryName(@"D:\1\2\"); //D:\1\2
string dir2 = Path.GetDirectoryName(Path.Combine(@"D:\1","Temp")); // D:\1
string dir3 = Path.GetDirectoryName(Path.Combine(@"D:\1\", "Temp")); // D:\1
}
string dir4 = Path.GetDirectoryName(@"\\192.168.0.2\share\file_1.txt"); // \\192.168.0.2\share
string root1 = Path.GetPathRoot(@"\\192.168.0.2\share\file_1.txt"); // \\192.168.0.2\share
string root2 = Path.GetPathRoot(@"192.168.0.2\share\file_1.txt"); // ""
string root3 = Path.GetPathRoot(@"//192.168.0.2\share\file_1.txt"); // \\192.168.0.2\share
string root4 = Path.GetPathRoot(@"//192.168.0.2\share\1\2file_1.txt"); // \\192.168.0.2\share
3、Flie类
File类,是一个静态类,主要是来提供一些函数库用的。静态实用类,提供了很多静态的方法,支持对文件的基本操作,包括创建,拷贝,移动,删除和打开一个文件。File类方法的参量很多时候都是路径path。File的一些方法可以返回FileStream和StreamWriter的对象。可以和他们配套使用。
System.IO.File类和System.IO.FileInfo类主要提供有关文件的各种操作,在使用时需要引用System.IO命名空间。下面通过程序实例来介绍其主要属性和方法。
File类常用的操作方法
1)、创建文件方法
//参数1:要创建的文件路径
File.Create(@"D:\Test\Debug1\测试.txt")
2)、打开文件方法
//参数1:要打开的文件路径,参数2:打开的文件方式
File.Open(@"D:\Test\Debug1\测试.txt",FileMode.Append)
3)、追加文件方法
//参数1:要追加的文件路径,参数2:追加的内容
File.AppendAllText(@"D:\Test\Debug1\测试.txt","哈哈");
4)、复制文件方法
//参数1:要复制的源文件路径,参数2:复制后的目标文件路径,参数3:是否覆盖相同文件名
File.Copy(@"D:\Test\Debug1\测试.txt", @"D:\Test\Debug2\测试1.txt", true);
5)、移动文件方法
//参数1:要移动的源文件路径,参数2:移动后的目标文件路径
File.Move(@"D:\Test\Debug1\测试.txt", @"D:\Test\Debug3\测试2.txt");
6)、删除文件方法(彻底删除)
//参数1:要删除的文件路径
File.Delete(@"D:\Test\Debug1\测试.txt");
7)、设置文件属性方法
//参数1:要设置属性的文件路径,参数2:设置的属性类型(只读、隐藏等)
File.SetAttributes(@"D:\Test\Debug1\测试.txt", FileAttributes.Hidden);
编码
常见编码
(1)、通用字符集(UCS)
ISO/IEC 10646-1 [ISO-10646]定义了一种多于8比特字节的字符集,称作通用字符集(UCS),它包含了世界上大多数可书写的字符系统。已定义了两种多8比特字节编码,对每一个字符采用四个8比特字节编码的称为UCS-4,对每一个字符采用两个8比特字节编码的称为UCS-2。它们仅能够对UCS的前64K字符进行编址,超出此范围的其它部分当前还没有分配编址。
(2)、基本多语言面(BMP)
ISO 10646 定义了一个31位的字符集。然而,在这巨大的编码空间中,迄今为止只分配了前65534个码位 (0x0000 到0xFFFD)。这个UCS的16位子集称为 “基本多语言面 ”(Basic Multilingual Plane, BMP)。
(3)、Unicode编码
历史上,有两个独立的,创立单一字符集的尝试。一个是国际标准化组织(ISO)的 ISO 10646 项目;另一个是由(一开始大多是美国的)多语言软件制造商组成的协会组织的 Unicode 项目。幸运的是, 1991年前后, 两个项目的参与者都认识到:世界不需要两个不同的单一字符集。它们合并双方的工作成果,并为创立一个单一编码表而协同工作。两个项目仍都存在并独立地公布各自的标准,但 Unicode 协会和 ISO/IEC JTC1/SC2 都同意保持 Unicode 和 ISO 10646 标准的码表兼容,并紧密地共同调整任何未来的扩展。Unicode 标准额外定义了许多与字符有关的语义符号学,一般而言是对于实现高质量的印刷出版系统的更好的参考。
(4)、UTF-8编码
特性:
1)UTF-8向UCS-4,UCS-2两者中任一个进行相互转换比较容易。
2)多8比特字节序列的第一个8比特字节指明了系列中8比特字节的数目。
3)8比特字节值FE和FF永远不会出现。
4)在8比特字符流中字符边界从哪里开始较容易发现。
(5)、UTF-7编码
UTF-7:A Mail-Safe Transformation Format of Unicode(RFC1642)。这是一种使用 7 位 ASCII 码对 Unicode 码进行转换的编码。它的设计目的仍然是为了在只能传递 7 为编码的邮件网关中传递信息。 UTF-7 对英语字母、数字和常见符号直接显示,而对其他符号用修正的 Base64 编码。符号 + 和 - 号控制编码过程的开始和暂停。所以乱码中如果夹有英文单词,并且相伴有 + 号和 - 号,这就有可能是 UTF-7 编码。
(6)GB2312编码
最早,表示汉字的区位码中,分为94个区,每个区94个汉字,1-15区是西文字符,图形等,16-5为一级汉字,56-87为二级汉字,87区以上为新字用。而我们在Windows默认的编码,GB2312(1981年国家颁布的《信息交换用汉字编码字符集基本集》)
字符串的编码转换:
string str = "abcd";//测试字符串
byte[] bytes = Encoding.GetEncoding("ascii").GetBytes(str);//将字符串转成ascii编码的字节数组,这里的bytes数组,长度为4,分别对应于abcd的ascii码97、98、99、100
string result = Encoding.GetEncoding("ascii").GetString(bytes);//将字节数组转回为字符串
Console.WriteLine(result);//输出abcd
注:这里应用到了ascii编码。我们知道,ascii码是国际标准编码,全称为:美国信息交换标准编码,只能表示127个字符,不能代表汉字,所以我们对汉字进行ascii编码之后,是不能进行还原的。汉字不能转变为ascii码,因此会变成乱码,对乱码进行还原也就还原不了了。正是由于ascii码的局限性,不能表示世界上各种语言和符号,因此ISO(国际标准化组织)推出了unicode编码,它可以容纳世界上所有的文字和字符。
有些时候,可能编码会是这样子的:\u4e2d\u56fd
而我们可以这样来处理:如下
string value = "\u4e2d\u56fd";
Console.WriteLine(Uri.UnescapeDataString(value));//输出:中国
项目开发中经常会有出现乱码的情况,这就是由于两端(服务端、请求端)编解码的方式不一致造成的。比如服务端是utf-8编码,而在客户端以gbk接收,那么就会出现乱码。所以解决乱码这个问题,思路就是从对方的编码方式入手,弄清楚对方的编码是什么编码,我这边就以什么编码来解码。这个解决问题的思路,在我实际项目开发过程中屡试不爽。
比如我们经常会用到web页面导出excel的问题。代码如下:
string fileName = HttpUtility.UrlEncode("Excel文件名为中文哦.xls");
Response.Clear();
Response.Buffer = true;
Response.AppendHeader("Content-Disposition", "attachment;filename=" + fileName);
Response.ContentEncoding = System.Text.Encoding.UTF8;
Response.ContentType = "application/vnd.ms-excel";
this.EnableViewState = false;
经过HttpUtility.UrlEncode方法进行编码之后,在IE浏览器下弹出的excel下载对话框中显示的文件名就不会显示乱码,而显示正常的汉字了。对应的方法是HttpUtility.UrlDecode方法,进行解密。这两个方法在web开发编解码当中会用到。
比如:
string text = "http://www.baidu.com/baidu?word=%D6%D0%B9%FA%B4%F3%B0%D9%BF%C6%D4%DA%CF%DF%C8%AB%CE%C4%BC%EC%CB%F7&tn=myie2dg";
string result = HttpUtility.UrlDecode(text, Encoding.GetEncoding("gbk"));
Response.Write(result);//输出http://www.baidu.com/baidu?word=中国大百科在线全文检索&tn=myie2dg
字符串存入txt文件如下:
//没有这个文件的话 会给你创建一个 有的话 会给你覆盖掉
string str="今天天气好晴朗处处好风光";
//需要将字符串转换成字节数组
byte[] buffer= Encoding.Default.GetBytes(str);
File.WriteAllBytes(@"C:\Users\SpringRain\Desktop\new.txt", buffer);
txt文件以行的形式读取:
string[] contents = File.ReadAllLines(@"C:\Users\SpringRain\Desktop\抽象类特点.txt", Encoding.Default);//所谓Encoding.Default,是指当前系统设置的“默认字符集编码方式”。你可以通过控制面板里面的区域选项设置它
foreach (string item in contents)
{
Console.WriteLine(item);
}
txt文件全部读取:
string str = File.ReadAllText("抽象类特点.txt", Encoding.Default);//相对路径,本运行程序目录下(也可绝对路径)
txt文件以行的形式写入:(覆盖原数据)
File.WriteAllLines(@"C:\Users\SpringRain\Desktop\new.txt", new string[] { "aoe", "ewu" });
txt文件写入:(覆盖原数据)
File.WriteAllLines(@"C:\Users\SpringRain\Desktop\new.txt", new string[] { "aoe", "ewu" });
txt文件追加写入:
十四、c#方法
一个方法是把一些相关的语句组织在一起,用来执行一个任务的语句块。每一个 C# 程序至少有一个带有 Main 方法的类。
要使用一个方法,您需要:
- 定义方法
- 调用方法
C# 中定义方法
当定义一个方法时,从根本上说是在声明它的结构的元素。在 C# 中,定义方法的语法如下:
<Access Specifier> <Return Type> <Method Name>(Parameter List)
{
Method Body
}
下面是方法的各个元素:
- Access Specifier:访问修饰符,这个决定了变量或方法对于另一个类的可见性。
- Return type:返回类型,一个方法可以返回一个值。返回类型是方法返回的值的数据类型。如果方法不返回任何值,则返回类型为 void。
- Method name:方法名称,是一个唯一的标识符,且是大小写敏感的。它不能与类中声明的其他标识符相同。
- Parameter list:参数列表,使用圆括号括起来,该参数是用来传递和接收方法的数据。参数列表是指方法的参数类型、顺序和数量。参数是可选的,也就是说,一个方法可能不包含参数。
- Method body:方法主体,包含了完成任务所需的指令集。
实例
下面的代码片段显示一个函数 FindMax,它接受两个整数值,并返回两个中的较大值。它有 public 访问修饰符,所以它可以使用类的实例从类的外部进行访问。
实例
class NumberManipulator
{
public int FindMax(int num1, int num2)
{
/* 局部变量声明 */
int result;
if (num1 > num2)
result = num1;
else
result = num2;
return result;
}
...
}
C# 中调用方法
您可以使用方法名调用方法。下面的实例演示了这点:
实例
using System;
namespace CalculatorApplication
{
class NumberManipulator
{
public int FindMax(int num1, int num2)
{
/* 局部变量声明 */
int result;
if (num1 > num2)
result = num1;
else
result = num2;
return result;
}
static void Main(string[] args)
{
/* 局部变量定义 */
int a = 100;
int b = 200;
int ret;
NumberManipulator n = new NumberManipulator();
//调用 FindMax 方法
ret = n.FindMax(a, b);
Console.WriteLine("最大值是: {0}", ret );
Console.ReadLine();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
最大值是: 200
您也可以使用类的实例从另一个类中调用其他类的公有方法。例如,方法 FindMax 属于 NumberManipulator 类,您可以从另一个类 Test 中调用它。
实例
using System;
namespace CalculatorApplication
{
class NumberManipulator
{
public int FindMax(int num1, int num2)
{
/* 局部变量声明 */
int result;
if (num1 > num2)
result = num1;
else
result = num2;
return result;
}
}
class Test
{
static void Main(string[] args)
{
/* 局部变量定义 */
int a = 100;
int b = 200;
int ret;
NumberManipulator n = new NumberManipulator();
//调用 FindMax 方法
ret = n.FindMax(a, b);
Console.WriteLine("最大值是: {0}", ret );
Console.ReadLine();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
最大值是: 200
递归方法调用
一个方法可以自我调用。这就是所谓的 递归。下面的实例使用递归函数计算一个数的阶乘:
实例
using System;
namespace CalculatorApplication
{
class NumberManipulator
{
public int factorial(int num)
{
/* 局部变量定义 */
int result;
if (num == 1)
{
return 1;
}
else
{
result = factorial(num - 1) * num;
return result;
}
}
static void Main(string[] args)
{
NumberManipulator n = new NumberManipulator();
//调用 factorial 方法
Console.WriteLine("6 的阶乘是: {0}", n.factorial(6));
Console.WriteLine("7 的阶乘是: {0}", n.factorial(7));
Console.WriteLine("8 的阶乘是: {0}", n.factorial(8));
Console.ReadLine();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
6 的阶乘是: 720
7 的阶乘是: 5040
8 的阶乘是: 40320
参数传递
当调用带有参数的方法时,您需要向方法传递参数。在 C# 中,有三种向方法传递参数的方式:
方式 | 描述 |
---|---|
值参数 | 这种方式复制参数的实际值给函数的形式参数,实参和形参使用的是两个不同内存中的值。在这种情况下,当形参的值发生改变时,不会影响实参的值,从而保证了实参数据的安全。 |
引用参数 | 这种方式复制参数的内存位置的引用给形式参数。这意味着,当形参的值发生改变时,同时也改变实参的值。 |
输出参数 | 这种方式可以返回多个值。 |
按值传递参数
这是参数传递的默认方式。在这种方式下,当调用一个方法时,会为每个值参数创建一个新的存储位置。
实际参数的值会复制给形参,实参和形参使用的是两个不同内存中的值。所以,当形参的值发生改变时,不会影响实参的值,从而保证了实参数据的安全。下面的实例演示了这个概念:
实例
using System;
namespace CalculatorApplication
{
class NumberManipulator
{
public void swap(int x, int y)
{
int temp;
temp = x; /* 保存 x 的值 */
x = y; /* 把 y 赋值给 x */
y = temp; /* 把 temp 赋值给 y */
}
static void Main(string[] args)
{
NumberManipulator n = new NumberManipulator();
/* 局部变量定义 */
int a = 100;
int b = 200;
Console.WriteLine("在交换之前,a 的值: {0}", a);
Console.WriteLine("在交换之前,b 的值: {0}", b);
/* 调用函数来交换值 */
n.swap(a, b);
Console.WriteLine("在交换之后,a 的值: {0}", a);
Console.WriteLine("在交换之后,b 的值: {0}", b);
Console.ReadLine();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
在交换之前,a 的值:100
在交换之前,b 的值:200
在交换之后,a 的值:100
在交换之后,b 的值:200
结果表明,即使在函数内改变了值,值也没有发生任何的变化。
按引用传递参数
引用参数是一个对变量的内存位置的引用。当按引用传递参数时,与值参数不同的是,它不会为这些参数创建一个新的存储位置。引用参数表示与提供给方法的实际参数具有相同的内存位置。
在 C# 中,使用 ref 关键字声明引用参数。下面的实例演示了这点:
实例
using System;
namespace CalculatorApplication
{
class NumberManipulator
{
public void swap(ref int x, ref int y)
{
int temp;
temp = x; /* 保存 x 的值 */
x = y; /* 把 y 赋值给 x */
y = temp; /* 把 temp 赋值给 y */
}
static void Main(string[] args)
{
NumberManipulator n = new NumberManipulator();
/* 局部变量定义 */
int a = 100;
int b = 200;
Console.WriteLine("在交换之前,a 的值: {0}", a);
Console.WriteLine("在交换之前,b 的值: {0}", b);
/* 调用函数来交换值 */
n.swap(ref a, ref b);
Console.WriteLine("在交换之后,a 的值: {0}", a);
Console.WriteLine("在交换之后,b 的值: {0}", b);
Console.ReadLine();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
在交换之前,a 的值:100
在交换之前,b 的值:200
在交换之后,a 的值:200
在交换之后,b 的值:100
结果表明,swap 函数内的值改变了,且这个改变可以在 Main 函数中反映出来。
按输出传递参数
return 语句可用于只从函数中返回一个值。但是,可以使用 输出参数 来从函数中返回两个值。输出参数会把方法输出的数据赋给自己,其他方面与引用参数相似。
下面的实例演示了这点:
实例
using System;
namespace CalculatorApplication
{
class NumberManipulator
{
public void getValue(out int x )
{
int temp = 5;
x = temp;
}
static void Main(string[] args)
{
NumberManipulator n = new NumberManipulator();
/* 局部变量定义 */
int a = 100;
Console.WriteLine("在方法调用之前,a 的值: {0}", a);
/* 调用函数来获取值 */
n.getValue(out a);
Console.WriteLine("在方法调用之后,a 的值: {0}", a);
Console.ReadLine();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果:
在方法调用之前,a 的值: 100
在方法调用之后,a 的值: 5
提供给输出参数的变量不需要赋值。当需要从一个参数没有指定初始值的方法中返回值时,输出参数特别有用。请看下面的实例,来理解这一点:
实例
using System;
namespace CalculatorApplication
{
class NumberManipulator
{
public void getValues(out int x, out int y )
{
Console.WriteLine("请输入第一个值: ");
x = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("请输入第二个值: ");
y = Convert.ToInt32(Console.ReadLine());
}
static void Main(string[] args)
{
NumberManipulator n = new NumberManipulator();
/* 局部变量定义 */
int a , b;
/* 调用函数来获取值 */
n.getValues(out a, out b);
Console.WriteLine("在方法调用之后,a 的值: {0}", a);
Console.WriteLine("在方法调用之后,b 的值: {0}", b);
Console.ReadLine();
}
}
}
当上面的代码被编译和执行时,它会产生下列结果(取决于用户输入):
请输入第一个值:
7
请输入第二个值:
8
在方法调用之后,a 的值: 7
在方法调用之后,b 的值: 8