0. C#的几个概念
- CLR(Common Language Runtime)公共语言运行库
- CLS(Common Language Specifiction)公共语言规范
- CTS(Common Language System)通用类型系统
1. C#的变量命名规则
- 变量的名以字母、下划线或@开头,后面可以跟字母、数字、下划线,而不能包含空格、标点符号、运算符等其它符号。
- 变量名不能与C#中的关键字名称相同。这些关键字我们在附录A中给出。
- 变量名不能与C#中的库函数名称相同
- 对大小写敏感
- 同一变量名不允许重复定义
int @_a = 10;//正确
int @xy = 10;//正确
int _xy = 20;//正确
int @123 = 30;//错误
int @_123 = 60;//正确
2. C#的数据类型
C#的基础数据类型并没有内置于C#语言中,而是内置于.net Framework中。
如,在c#中声明一个int类型的数据时,声明的实际上是.net结构System.Int32的一个实例。这听起来似乎很深奥,但其意义深远:这表示在语法上,可以把所有的基础数据类型看作是支持某些方法的类。类型实际上仍存储为基本类型。基本类型在概念上用.Net结构表示,所以肯定没有性能损失。
类型 | 描述 | 默认值 |
---|---|---|
bool | 布尔值 | False |
byte | 8位无符号正数 | 0 |
char | 16位字符 | ‘\0’ |
short | 16位有符号整型 | 0 |
int | 32位整型有符号 | 0 |
float | 32位单精度 | 0.0F |
long | 64位整形有符号 | 0L |
double | 64位双精度浮点型 | 0D |
sbyte | 8位有符号 | 0 |
short | 16位有符号 | 0 |
unit | 32位无符号 | 0 |
ushort | 16位无符号 | 0 |
ulong | 64位无符号 | 0 |
decimal | 128位精度十进制 | 0.0M |
sizeof关键字
sizeof([数据类型])
: 返回类型的字节数
3. C#的值传递和引用传递
- out修饰入参
被out修饰的参数在方法中要是用的话必须先初始化再使用. - ref修饰入参
被ref修饰的相当于C语言的指针. - 方法调用传递实参的时候必须使用ref和out修饰.
static void ex1(ref int a, out int b)
{
b = a*10;
a = 10 + a;
}
public static void Main(string[] args)
{
int a = 10, b = 0;
ex1(ref a, out b);//使用ref和out修饰
Console.WriteLine(a+","+b);//20,100
Console.ReadKey();
}
4. C#的类型转换
4.1 强转
该转换方式主要用于数字类型之间的转换,从int类型向long,float,double,decimal 类型转换可以使用隐式转换,
但从long型到int 就需要使用显示转换,即使用该类型的转换方式否则产生编译错误。
该方式对于浮点数会无条件的舍去,会失去精确度
对于char类型的到int类型的转换,传回的值是ASCII码.
int i1 = 10;
long l1 = 1000;
float f1 = 1234567890.0F;
double d1 = 1234567890.0D;
Console.WriteLine((int)l1);//1000
Console.WriteLine((int)f1);//1234567936
Console.WriteLine((int)d1);//1234567890
4.2 int.Parse([string 变量名])
该方式是将数字内容的字符串转换为int类型,如果字符串的内容为Null ,则抛出ArgumentNullException异常;如果字符串内容不是数字,则抛出FormatException异常。使用该方法只能处理字符串的内容,而且转换后的字符串内容要在int类型的可表示范围之内.
string s1 = "100";
Console.WriteLine(int.Parse(s1));//OK
string s2 = "100a";
Console.WriteLine(int.Parse(s2));//转换失败报错
4.3 int.TryParse(string s, out int result)
该方式也是将数字内容的字符串转换为int类型,但是该方式有比int.Parse 优越的地方,就是它不会出现异常,最后一个参数result是输出值,如果转换成功则输出相应的值,转换失败则输出0。
string s2 = "1000a";
int result;
Console.WriteLine(int.TryParse(s2, out result));//False
Console.WriteLine(result);//0
4.4 Convert对象
提供了ToXxx([被转换的数据])一系列静态方法.
5. 集合
- 非泛型集合的类和接口位于System.Collections命名空间。
- 泛型集合的类和接口位于System.Collections.Generic命名空间。
非泛型集合接口 | 泛型集合接口 | 说明 |
---|---|---|
ICollection | ICollection<T> | 定义所有集合的大小(Count),枚举器(foreach)和同步(copyto)方法,继承自IEnumerable |
IList | IList<T> | 表示可按照索引单独访问的一组对象(像数组一样) |
IDictionary | IDictionary<T> | 表示键/值对的集合 |
IComparer | IComparer<T> | 定义类型为比较两个对象而实现的方法 |
IEqualityComparer | IEqualityComparer<T> | 定义方法以支持对象的相等比较 |
IEnumerable | IEnumerable<T> | 公开枚举器。实现了该接口意味着允许foreach语句循环访问集合中的元素 |
IEnumerator | IEnumerator<T> | 支持在泛型集合上进行简单迭代 |
5.1 ArrayList
/* ------- 测试ArrayList ------ */
public static void Main1(String[] agrs)
{
// 1. ArrayList
//可重复添加元素,先行表结构
ArrayList alist = new ArrayList();
alist.Add("abc");
alist.Add("abc");
alist.Add(1230);
//alist.RemoveAt(0);//删除索引为0的元素
//alist.Remove(1230);//删除指定对象元素
//alist.RemoveRange(0, 2);//删除[0,2)的元素
alist.Insert(0, 1000);//插入一个对象
ArrayList a = new ArrayList();
a.Add(000);
a.Add(111);
alist.Insert(0, a);//插入一个集合
//判断是否包含指定元素
Console.WriteLine("是否包含{0}元素:{1}", 1230, alist.Contains(1230));
//增强for循环遍历ArrayList集合
foreach (var item in alist)
{
Console.WriteLine(item);
}
Console.ReadKey();
}
5.2 HashTable
public static void Main2(String[] agrs)
{
///无序,不能重复
Hashtable ht = new Hashtable();
ht.Add("one", 100000);
ht.Add("two", 12000);
Persons p = new Persons();
ht.Add(p, 13000);
//判断是否包含某个 key
Console.WriteLine("是否包含该 key:{0} ?:{1}", "one", ht.Contains("one"));
//添加重复的key对应的value
//ht.Add("one", "the new one value");//会报错
ht["one"] = "the new one value";//这样才可以
//ht.Remove("one");//清楚指定 key 的元素
//ht.Clear();//清空
foreach (var key in ht.Keys)
{
Console.WriteLine("key:{0},value:{1}", key, ht[key]);
}
Console.ReadKey();
}
5.3 Dictionary
/* ------ 3. 测试 Dictionary ------ */
public static void Main3(String[] args)
{
Dictionary<int, string> dict = new Dictionary<int, string>();
dict.Add(1, "one");
dict.Add(2, "two");
dict.Add(3, "three");
dict.Add(4, "four");
dict.Add(5, "five");
dict.Add(6, "six");
//遍历
foreach (var key in dict.Keys)
{
Console.WriteLine("key:{0},value:{1}", key, dict[key]);
}
//第二种遍历方式
Console.WriteLine("***********************");
//类似Java的Map的EntrySet
foreach (KeyValuePair<int, string> item in dict)
{
Console.WriteLine("key:{0},value:{1}", item.Key, item.Value);
}
Console.ReadKey();
}
6. 多态
6.1 new关键字
- 子类不想重写父类的方法的时候使用new关键字修饰方法.
- 在用作修饰符时,new关键字可以显式的隐藏从基类继承的成员。隐藏继承的成员时,该成员的派生版本将替换基类版本。虽然可以不使用new修饰符的情况下隐藏成员,但会生成警告。如果使用new显示隐藏成员,则会取消此警告,并记录要替换为派生版本这一事实。
class Person
{
string _name;
public string Name
{
set { this._name = value; }
get { return this._name; }
}
public Person(string name)
{
this._name = name;
}
public void say() {
Console.WriteLine("我是人类say2()");
}
}
class Chinese : Person
{
public Chinese(string name) : base(name) { }
//子类不想重写父类的该方法,即使用new关键字来隐藏父类的say方法
public new void say() {
Console.WriteLine("我是中国人say2(...)");
}
}
public static void Main(string[] args)
{
Person pc1 = new Chinese("小明");
pc1.say();//我是人类say2()
}
6.2 virtual和override
只有被virtual,abstract,override修饰的方法可以被子类重写,达到多态的效果.
class Person
{
string _name;
public string Name
{
set { this._name = value; }
get { return this._name; }
}
public Person(string name)
{
this._name = name;
}
//父类的方法被 virtual 修饰
public virtual void say()
{
Console.WriteLine("我是人类,我叫:{0}",this.Name);
}
}
class Chinese : Person
{
public Chinese(string name) : base(name) { }
//子类重谢父类的 virtual 修饰的方法
public override void say()
{
Console.WriteLine("我是中国人,我叫:{0}", this.Name);
}
}
public static void Main(string[] args)
{
Person pc1 = new Chinese("小明");
pc1.say();//我是中国人,我叫:小明
}
6.3 abstract 修饰符
和Java中一样abstract修饰符修饰的为抽象类,和抽象方法.
//定义一个抽象类,抽象类不允许new出来和Java一样
public abstract class Animal
{
public Animal() { Console.WriteLine("抽象类Animal的构造函数"); }
public abstract void Bark();//抽象方法不允许实现和Java一样
}
public class Cat : Animal
{
public override void Bark()
{
Console.WriteLine("Cat 对抽象类 Animal 的实现!");
}
}
//虽然抽象类不能直接new但是和Java一样在子类初始化的时候仍然后初始化抽象类
Animal cat = new Cat();
cat.Bark();//Cat 对抽象类 Animal 的实现!
7. IO操作
using System.IO;
7.1 File 类
- 读取所有的字节返回一个字节数组
File.ReadAllBytes(@"E:\\test.txt");
// 1. 读取所有的字节返回一个字节数组
byte[] buff = File.ReadAllBytes(@"E:\\test.txt");
string res = Encoding.Default.GetString(buff);
Console.WriteLine(res);
- 写入字节数组到指定路径
File.WriteAllBytes(@"E:\\test1.txt",buff);
// 1.2 写入字节数组到指定路径
File.WriteAllBytes(@"E:\\test1.txt",buff);
- 读取所有的行,指定编码格式为默认ansc
string[] lines = File.ReadAllLines(@"E:\\test.txt",Encoding.Default);
//2.读取所有的行,指定编码格式为默认aisn
string[] lines = File.ReadAllLines(@"E:\\test.txt",Encoding.Default);
Console.WriteLine(lines[0]);
- 读取整个文档的文本内容
string content = File.ReadAllText(@"E:\\test.txt", Encoding.Default);
// 3.读取整个文档的文本内容
string content = File.ReadAllText(@"E:\\test.txt", Encoding.Default);
7.2 FileStream 字节读写类
FileStream(@"E:\\test.txt",FileMode.OpenOrCreate,FileAccess.Read);
:读方式
Read(buff, 0, buff.Length)
: 从流中读取字到byte数组
FileStream(@"E:\\test3.txt", FileMode.OpenOrCreate, FileAccess.Write);
:写方式
Write(buff, 0, len) :
写入byte数组内容到流
/* ------- 2. FileStream ------- */
public static void Main2(String[] args)
{
//文件路径,打开方式,文件操作方式
FileStream fsRead = new FileStream(@"E:\\test.txt",FileMode.OpenOrCreate,FileAccess.Read);
FileStream fsWrite = new FileStream(@"E:\\test3.txt", FileMode.OpenOrCreate, FileAccess.Write);
byte[] buff = new byte[1024];
int len = 0;
while ((len = fsRead.Read(buff, 0, buff.Length)) != 0)
{
string str = Encoding.Default.GetString(buff, 0, len);
Console.WriteLine(str);
fsWrite.Write(buff, 0, len);
}
Console.WriteLine("OK");
//关闭流
fsRead.Close();
fsWrite.Close();
//释放资源
fsRead.Dispose();
fsWrite.Dispose();
Console.ReadKey();
}
//测试自定义的方法
public static void Main3(string[] args)
{
copyFile(@"E:\\test3.txt",@"E:\\text4.txt");
}
//使用 FileStream 封装复制文件的方法
public static void copyFile(string source,string target)
{
//使用 using 来管理文件流的关闭和释放
using (FileStream fsRead = new FileStream(source, FileMode.OpenOrCreate, FileAccess.Read))
{
using (FileStream fsWrite = new FileStream(target, FileMode.OpenOrCreate, FileAccess.Write))
{
byte[] buff = new byte[1024];
int len = 0;
while ((len = fsRead.Read(buff, 0, buff.Length)) != 0)
{
fsWrite.Write(buff, 0, len);
}
Console.WriteLine("OK");
}
}
}// end of copyFile()
7.3 StreamReader 字符读写类
StreamReader(@"E:\\text4.txt", Encoding.Default)
读,指定编码格式
EndOfStream
: 判断是否到达流的结尾ReadLine()
: 读取一行
StreamWriter(@"E:\\test5.txt",true)
写,第二个参数是否追加
WriteLine(str)
: 写入一行
/* -------- 3. StreamReader 字符读写------------ */
public static void Main4(String[] args)
{
using(StreamReader sr = new StreamReader(@"E:\\text4.txt", Encoding.Default))
{
using (StreamWriter sw = new StreamWriter(@"E:\\test5.txt",true))
{
//如果没到流的结尾
while (!sr.EndOfStream)
{
string str = sr.ReadLine();
Console.WriteLine(str);
sw.WriteLine(str);
}
}
}
Console.WriteLine("OK");
Console.ReadKey();
}
8. 访问修饰符
- public : 公开的公共的
- private : 私有的只能在当前类的内部访问的
- protected : 受保护的,只能在当前类的内部以及该类的子类中访问
- internal : 只能在当前的项目中访问,在同一个项目中,internal和public的权限是一样的
protected internal : protected + internal
- 能修饰类的访问权限只有两个:public,internal
- 可访问性不一致问题:子类的访问权限不能高于父类的访问权限,会暴父类的成员
9. 序列化和反序列化
类似Java中的序列化.
1. BinaryFormatter bf = new BinaryFormatter();
:创建序列化对象
- bf.Serialize(fw, man);
//第一个是输出流,第二个是被序列化的对象
2. BinaryFormatter bf = new BinaryFormatter();
:创建反序列化对象
- Man man2 = (Man)bf.Deserialize(fr);
//fr为序列化的文件输出流
[Serializable]//标志位表示该类可被序列化
public class Man
{
public string Name { get; set; }
public string Gender { get; set; }
public int Age { get; set; }
}
static void Main(string[] args)
{
Man man = new Man();
man.Name = "张三";
man.Age = 15;
man.Gender = "男";
//创建输出流
using (FileStream fw = new FileStream(@"E:\\ManObj.txt",FileMode.OpenOrCreate,FileAccess.Write))
{
//创建序列化对象
BinaryFormatter bf = new BinaryFormatter();
//开始序列化
bf.Serialize(fw, man);//第一个是输出流,第二个是被序列化的对象
}
Console.WriteLine("序列化OK");
//反序列化,先创建输入流
using (FileStream fr = new FileStream(@"E:\\ManObj.txt",FileMode.Open,FileAccess.Read))
{
//创建序列化对象
BinaryFormatter bf = new BinaryFormatter();
//开始反序列化
Man man2 = (Man)bf.Deserialize(fr);
Console.WriteLine(man2.Name);
Console.WriteLine(man2.Age);
Console.WriteLine(man2.Gender);
}
Console.WriteLine("反序列化OK");
Console.ReadKey();
}
10. 密封类partial
密封类,可以继承其他类,但是无法被其他类继承
//被 partial 修饰的类表示部分类
partial class Person
{
private string _name = "bart";
}
partial class Person
{
void say()
{
//相当于同一个类,可以使用上面一个部分类的成员
Console.WriteLine("部分类调用自己的成员:{0}",_name);
}
}
11. 接口
接口的特点:
1. 接口不允许使用任意修饰符默认就是 public
2. 接口可以继承多个接口
3. 接口可以有成员属性
4. 子类继承的父类和接口中的方法重名的时候显示使用接口的方法,并重写IFlayable.say(){XXXX;}
namespace _08接口特点
{
class Program
{
/// <summary>
/// 接口的特点:
/// 1. 接口不允许使用任意修饰符默认就是 public
/// 2. 接口可以继承多个接口
/// 3. 接口可以有成员属性
/// 4. 子类继承的父类和接口中的方法重名的时候显示使用接口的方法,并重写
/// IFlayable.say(){XXXX;}
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
IFlyable ifa = new SuperMane();
ifa.Fly();
Bird b = new SuperMane();
b.Fly();
Console.ReadKey();
}
}
//继承类同时实现接口
public class SuperMane : Bird ,IFlyable
{
new public void Fly()
{
Console.WriteLine("我是超人,我会飞,来自父类");
}
//显示实现接口的同名方法
void IFlyable.Fly()
{
Console.WriteLine("我是超人,我会飞,来自接口");
}
}
public class Bird
{
public void Fly()
{
Console.WriteLine("我是鸟,我会飞!");
}
}
//默认是public修饰,I开头able结尾
public interface IFlyable
{
//接口的方法不能被 public 修饰,因为它本身就默认是 public 的
void Fly();
//不允许有方法体的方法
//string Name { get; set; }
}
}