在这里,我将介绍一些关于字符串,linq,json,xml,socket方面的知识,当然,也是为了制作游戏储备的知识,希望能帮助到看到这篇博客的朋友,博客有点乱,抱歉了
C#学习高级篇
这里我将对之前提到的c#初级和中级进行进一步扩展介绍
01— 字符串
这里我们将介绍字符串的几种基本应用
1. 字符串是string类型的,值应该用双引号修饰
2. 我们输出字符串.length,即可获得字符串的长度
3. 我们可以对字符串进行拼接,但是这里修改的是字符串在堆中地址的引用
字符串的值本身不会被改变
4. 我们可以根据索引来获得字符串中的字符
5. 我们可以以数组的方式堆字符串进行循环遍历输出
namespace _01_string
{
class Program
{
static void Main(string[] args)
{
string s = "www.wenxue.com";//首先,我们新建一个字符串
int a = s.Length;//这里,我们获得字符串的长度
s = "http://" + s;//这里是字符串的拼接
// Console.Write(s);
char c = s[0];//这里是根据索引获得字符串中字符
for (int i = 0; i < s.Length; i++)
{
Console.Write(s[i]);//遍历字符串中的字符并且输出
}
Console.Write(c);
//Console.Write(a);
Console.ReadKey();
}
}
}
02——字符串的一些常用方法
这里我们将介绍一些字符串的常用方法
1. CompareTo方法,这个是比较两个字符串的方法,如果字符串相等,就会返回0,如果不想等,就会按照顺序比较第一个不相同的值,如果比较的值在字母表顺序前面,就会返回-1,如果在后面就会返回1
2. Replace方法,这个方法,你给定两个字符,就会用后面的字符替换掉前面的字符,如果两个字符串,就会用后面的字符串替换掉前面的字符串
3. Spilt方法,这个方法,会根据你给定的分隔符来生成新的子数组来进行存储
4. subString方法,这个方法,会根据你给的区间截取字符串,如果只给定一个索引,就会截取索引到字符串结尾的字符串
5. Trim方法,会删掉字符串开头和结尾的空格,多用于用户注册
6. IndexOf方法,这个方法会和你给出的字符串进行比较,如果不想等,就返回-1,如果相等的话,就会返回匹配字符串在整体字符串中首字母的索引
7. ToUpper方法,会将字符串中的英文字母大写;
using System;
usingSystem.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _02_stringAdd
{
class Program
{
static void Main(string[] args)
{
//这里我们将介绍string字符串的几种常用方法
string s = " www.wenxue.com";
// string s2 = "www.wenque.com";
//int a= s.CompareTo(s2);
// Console.WriteLine(a);
//String newStr = s.Replace('.', '_');
//Console.WriteLine(newStr);
//string[] a = s.Split('.');
//foreach(var temp in a)
//{
// Console.WriteLine(temp);
//}
//string str= s.ToUpper();
// Console.WriteLine(str);
//string str=s.Trim();
//Console.WriteLine(str);
//string str=s.Substring(4,7);
//Console.WriteLine(str);
int a=s.IndexOf("wenxue");
Console.WriteLine(a);
Console.ReadKey();
}
}
}
003——StringBuilder的学习
当我们一个字符串的内容经常发生改变的时候,我们可以使用StringBuilder来存储字符串内容,接下来介绍三种构造方式
1. 直接传递一个字符串
2. 输入一个数字,系统会构建一个数字大小的空间用来存储信息
3. 提供一个字符串和数字,这里当字符串增加到超过给定内存空间的时候,会将原有空间扩大到两倍直到能存储字符串为止
namespace _03_StringBuilder学习
{
class Program
{
static void Main(string[] args)
{
//这里我们将介绍StringBuilder的三种构造方式
//1
//StringBuilder sb = newStringBuilder("www.wenxue.com");
//2
// StringBuilder sb = new StringBuilder(20);
//3
StringBuilder sb = new StringBuilder("www.wenxue.com", 100);
}
}
}
004-string和stringBuilder的区别
当我们想要对一个字符串进行修改时,可以有以下两种操作
1.这两种都可以对内存进行修改,但是string直接修改的话,对于性能要求更高,而且操作也不便利,而StringBulider可以很方便的修改字符串的内容
static void Main(string[] args)
{
// StringBuilder sb = new StringBuilder("www.wenxue.com");
// sb.Append("xxx/html");
// Console.WriteLine(sb);
// Console.ReadKey();
string s = "www.wenxue.com";
s += "xxx/html";
Console.WriteLine(s);
Console.ReadKey();
}
005——StringBuilder的一些方法
当然我们首先要知道StringBuilder是属于System.Text命名空间下的方法,要查看它拥有
的方法可以按f12进行查询,这里介绍两个常用方法
1. insert,在给定索引处插入字符串,原有的字符串向后移动插入字符串的长度个位置
2. Replace方法,和String 的Repalce方法类似,都是将选定的字符串或者字符替换成自己指定的字符串或者字符
namespace _03_StringBuilder学习
{
class Program
{
static void Main(string[] args)
{
StringBuilder sb = new StringBuilder("www.wenxue.com");
sb.Insert(0, "http://");
sb.Replace(".", "");
// sb.Append("xxx/html");
Console.WriteLine(sb);
Console.ReadKey();
Console.ReadKey();
}
}
}
006-正则表达式之开始与结束的元字符
这里介绍正则表达式中两个用来检索的元字符^和$,这两个一个是定位到字符串的开始,一个是定位到字符串的结尾
namespace _04_正则表达式学习
{
class Program
{
static void Main(string[] args)
{
string s = "I am blue cat";
/*String res=Regex.Replace(s,"^","开始");*///这里是正则表达式中寻找开始位置的元字符,我们可以用它替换开始位置
string res = Regex.Replace(s, "$", "结束");
Console.Write(res);
Console.ReadKey();
}
}
}
007_正则表达式中数字校验符的运用
当我们想限制用户输入的密码规格时候,我们可以使用正则表达式很方便的实现
例如下面我们想要输入任意个纯数字为限制,我们可以给正则表达式加上这样的限制
^代表开头,$代表结尾,/d代表数组,这个要用@进行转义,*代表0到任意个数量
namespace _04_正则表达式学习
{
class Program
{
static void Main(string[] args)
{
string s = Console.ReadLine();
string Pattern = @"^\d*$";
bool isMatch=Regex.IsMatch(s, Pattern);
Console.Write(isMatch);
Console.ReadKey();
}
}
}
008——正则表达式中的转义字符
这里我介绍一下正则中的转义字符,转义字符有很多,到使用的时候可以自行百度,这里介绍一个过滤用的正则表达式@[^+任意字符],这个就代表了除了这方括号内字符以外的其他字符,然后我们替换的时候用的这个正则表达式,就会对这些字符以外的字符进行替换
namespace _04_正则表达式学习
{
class Program
{
static void Main(string[] args)
{
string s = "I am a cat";
string Pattern = @"[^amou]";
string res=Regex.Replace(s,Pattern,"*");
Console.WriteLine(res);
Console.ReadKey();
}
}
}
009——关于正则表达式中重复性描述字符
当我们需要对输入值进行重复性匹配,就需要用到重复性描述字符,比如这里,我们要筛选出一个5到12位大小的qq号,我们就用@(“^{5,12}$”)这个正则表达式进行匹配就行了
namespace _04_正则表达式学习
{
class Program
{
static void Main(string[] args)
{
string qq1 = "2384789";
string qq2 = "2384789839874938";
string qq3 = "d2384789";
string pattern = @"^\d{5,12}$";
Console.WriteLine(Regex.IsMatch(qq1, pattern));
Console.WriteLine(Regex.IsMatch(qq2, pattern));
Console.WriteLine(Regex.IsMatch(qq3, pattern));
Console.ReadKey();
}
}
}
010正则表达式——择一运算符
这里我们将介绍正则表达式中的择一运算符,即其中满足条件的都可以进行匹配
1. 匹配数字或者字母并输出,这里用MatchColliction对它们进行分割和存储
2. 匹配,;。其中的一个,都可以对该字符串进行分割和存储,用到spilt
class Program
{
static void Main(string[] args)
{
//string s1 ="2374e9sdhsdiuhe{iuuhwu@652";
//string pattern = @"\d|[a-z]";
//MatchCollection match= Regex.Matches(s1,pattern);
//foreach (Match match1 in match )
//{
// Console.Write(match1);
//}
//Console.ReadKey();
string s2 = "zhangsan,lisi,wangwu,zhaoliu";
string pattern = "[,]|[;]|[.]";
string[] s3 = Regex.Split(s2, pattern);
foreach (var s4 in s3)
{
Console.WriteLine(s4);
}
Console.ReadKey();
}
}
011正则表达式中的分组问题
这里解决的是重复检验字符,当我们输入a{2}这种,就代表将它重复两次
class Program
{
static void Main(string[] args)
{
string s1 = Console.ReadLine();
string pattern = @"(ab\w{2}){2}";
string res = Regex.Replace(s1, pattern, "22");
Console.WriteLine(res);
Console.ReadKey();
}
}
012.委托的初步介绍
我们可以通过委托去从外部调用方法,也就是当我们把一个方法当成一个参数去调用的时候就可以用到委托
比如这里,我们定义了一个委托方法abc,我们要调用其中的x.ToString方法,就可以用到委托,效果和直接输出是一样的
class Program
{
private delegate string abc();
static void Main(string[] args)
{
int x = 40;
abc s = new abc(x.ToString);
string f = s();
Console.WriteLine(f);
Console.ReadKey();
}
}
013——委托中在将方法当作参数进行传递
这里就实现了将方法当作参数进行传递,在外部定义委托,然后实现间接调用
class Program
{
private delegate void PrintString() ;
static void PrintStr(PrintString print)
{
print();
}
static void Main(string[] args)
{
//因为变量要有一个初始值才能调用,因此我们传递一个参数method1
PrintString method=Method1;
PrintStr(method);
}
static void Method1()
{
Console.WriteLine("method1");
}
static void Method2()
{
Console.WriteLine("method2");
}
}
014——action委托
Vs提供了一个内置(预编译)的action委托,我们也可以通过这个委托来调用外部方法,比如这里我们在外部定义了一个没有返回值,有两个参数的方法PrintDouble,然后在Main里声明Action<int,int>就可以调用了,action提供了一个可以接受外部参数的<>,这里最多包含16个类型
class Program
{
static void PrintDoubleInt(int a,int b)
{
Console.WriteLine(a + b);
}
static void Main(string[] args)
{
//因为变量要有一个初始值才能调用,因此我们传递一个参数method1
Action<int, int> a = PrintDoubleInt;
a(22, 23);
}
}
015—Func委托
Func委托也是一个预编译的委托,<>里包括的是参数类型和返回值类型,可以包含0-16个,顺序按照代码顺序,但是最后一个一定是返回值类型
class Program
{
static string PrintStr(String str)
{
Console.WriteLine(str);
return "哈哈";
}
static void Main(string[] args)
{
Func<string, string> a = PrintStr;
Console.WriteLine(a("666"));
Console.ReadKey();
}
}
016——int的冒泡排序
这里介绍一下Int冒泡排序的做法
我们给定一个bool值判断是否进行了排序,当排序不再进行,代表已经完成了排列,我们首先定义位true,表示排序没完成,如果不再交换位置,则位false,跳出do,while循环,然后在主函数中调用即可
class Program
{
static void sortArray(int[] sortArray)
{
bool isSwap = true;
for (int i = 0;i < sortArray.Length - 1;i++)
{
do
{
isSwap = false;
if (sortArray[i] > sortArray[i + 1])
{
int temp = sortArray[i];
sortArray[i] =sortArray[i + 1];
sortArray[i + 1] =temp;
isSwap = true;
}
}
while (isSwap);
}
}
static void Main(string[] args)
{
int[] array = new int[]{ 132,4353,213,435,667 };
sortArray(array);
foreach (var s in array)
{
Console.Write(s + " ");
}
Console.ReadKey();
}
}
}
017:用委托的方法对于任意类型的类进行排序
上面的排序都是对Int进行排序,我们怎么做可以复用这个方法呢?
每个类型的数据我们的比较方法都可能不同,这里我们写一个雇员类,它通过比较薪水大小来进行排序,我们通过bool属性来判断是否进行完了排序,true为完成,false为没完成
然后我们重写一下toString方法,使得排序输出的是它的名字和薪水
然后我们在Program里提供一个泛型方法叫做CommonSort,这里给定一个泛型数组,并且定义一个委托用来调用各个类型的比较方法,通过返回bool值判断是否排序完成
这样我们在Main方法里提供一个Employee类型的数组,调用CommonSort进行排序后再遍历输出该数组的ToString方法即可
using System;
usingSystem.Collections.Generic;
using System.Linq;
using System.Text;
usingSystem.Threading.Tasks;
namespace int冒泡排序
{
public class Employee
{
public string Name { get; private set; }
public int Salary { get; private set; }
public Employee(string name,int salary)
{
this.Name = name;
this.Salary = salary;
}
public static bool CompareSalary(Employee e1,Employee e2)
{
if (e1.Salary > e2.Salary) return true;
else return false;
}
public override string ToString()
{
return Name + ":" + Salary;
}
}
}
using System;
usingSystem.Collections.Generic;
using System.Linq;
using System.Text;
usingSystem.Threading.Tasks;
namespace int冒泡排序
{
class Program
{
static void CommonSort<T>(T[] sortArray, Func<T, T, bool> compareMethod)
{
bool isSwap = true;
for (int i = 0; i < sortArray.Length - 1; i++)
{
do
{
isSwap = false;
if (compareMethod(sortArray[i], sortArray[i + 1]))
{
T temp =sortArray[i];
sortArray[i] =sortArray[i + 1];
sortArray[i + 1] =temp;
isSwap = true;
}
}
while (isSwap);
}
}
static void Main(string[] args)
{
Employee[] employees = new Employee[]
{
new Employee("ejde",27),
new Employee("er342jde", 392),
new Employee("ejdrwere", 272),
new Employee("ejfdsde", 732),
new Employee("ejfede", 2726),
new Employee("ejtr5de", 2732)
};
CommonSort<Employee>(employees, Employee.CompareSalary);
foreach (var s in employees)
{
Console.WriteLine(s.ToString());
}
Console.ReadKey();
}
}
}
018——多播委托
我们知道委托不仅仅可以调用一个方法,还可以调用多个方法
这是一种调用的方法,这里的话有两点要注意
1.如果方法出现异常的话,多播委托的调用就会到异常位置停止
class Program
{
static void Test1()
{
Console.WriteLine("test1");
}
static void Test2()
{
Console.WriteLine("test2");
}
static void Main(string[] args)
{
Action a = Test1;
a += Test2;
a();
Console.ReadKey();
}
}
3. 还有一种调用方式
先获得action中的所有方法存放到delegates中,再用foreach遍历调用,这种方法调用的可以传递根据方法需求传递参数
class Program
{
static void Test1()
{
Console.WriteLine("test1");
}
static void Test2()
{
Console.WriteLine("test2");
}
static void Main(string[] args)
{
Action a = Test1;
a += Test2;
Delegate[] delegates = a.GetInvocationList();
foreach (Delegate de in delegates)
{
de.DynamicInvoke();
}
Console.ReadKey();
}
}
1. }
019.匿名方法
这个我也不是很清楚什么地方用,书写方法就如下,可以定义一个没有名字的方法,用来精简代码量
namespace _05_委托的学习
{
class Program
{
static void Main(string[] args)
{
//匿名方法本质是一个没有名字的方法,任何使用委托方法的地方都可以用匿名方法赋值
Func<int, int, int> test = delegate (int arg1, int arg2)
{
return arg1 + arg2;
};
}
}
}
020——Lamb表达式
在C#3.0后,只要是有委托参数的地方,我们就可以使用Lamb表达式对于代码进行简化,这个表达式可以用来替换掉匿名方法,直接定义委托给定方法参数就可以进行调用
class Program
{
static void Main(string[] args)
{
//Lambda表达式
Func<int, int, int> sum = (a, b) => { int temp = a + b; return temp; };
int res = sum(20, 30);
Console.WriteLine(res);
}
}
021—设计模式,观察者模式,猫捉老鼠
这里我们用到委托方法来实现观察者模式
首先,我们要介绍一下什么是观察者模式
观察者模式就是当我们触发某个事件的时候,我们希望其他的事件也被同步进行调用,比如这里的猫捉老师的例子,猫是被观察者,老鼠是观察者,当我们触发猫来的事件时,老鼠就会触发逃跑事件
首先我们在猫和老鼠两个类里定义猫跑和老鼠跑的方法,然后在猫类里定义一个委托用来重载调用老鼠跑的事件,最后在Program类里,当我们里将老鼠跑的方法赋值给这个委托,当触发猫来了的事件,就会调用这个委托
class Program
{
static void Main(string[] args)
{
Cat cat = new Cat("叮当猫", "白色");
Mouse mouse1 = new Mouse("杰瑞", "黑色");
cat.catRun += mouse1.MouseRun;
Mouse mouse2 = new Mouse("米奇", "绿色");
cat.catRun += mouse2.MouseRun;
cat.CatRun();
Console.ReadKey();
}
}
class Cat
{
private string color;
private string name;
public Cat(string name,string color)
{
this.color = color;
this.name = name;
}
public void CatRun()
{
Console.WriteLine(color + "的" + name + "发出喵喵喵的声音飞快赶过来");
if (catRun != null)
catRun();
}
public Action catRun;
}
class Mouse
{
private string color;
private string name;
public Mouse(string name,string color){
this.color = color;
this.name = name;
}
public void MouseRun()
{
Console.WriteLine(color + "的" + name + "说猫来了,溜了溜了");
}
}
022——对设计模式进行优化
一般来说,我们不希望在外部能够调用这个委托方法,所以我们一般会给委托添加一个event,这样事件就只会在类的内部发生改变,而不会在外部被调用,委托一般是用来注册和触发其他事件的,加上event修饰会更好
023——LinQ查询
这里介绍一下LinQ的用法和几种扩展方法
LinQ是C#中用来检索数据的一种语句,这里我介绍一个武林高手的例子
这里我们先定义一个武林高手类和一个功夫类,当然武林高手类里的数据要记得格式化一下,不然无法输出
然后我们在Program里先输入两者的数据,然后进行检索
第一种方法
这种写法和sql类似,也是检索级别大于9的角色,然后用foreach遍历输出res即可
Var res=from m in master
Wherelevel>9;
Select m;
using System;
第二种,外部定义个方法然后调用
我么在test1里处理了一个判断,通过bool值判断数据是否该被过滤出来,然后用where方法对它进行调用
第三钟,写一个Lamp表达式进行处理
这种方法比较简单,就是直接用lamp表达式进行判断即可
这也是检索等级大于9的数值
第四种:介绍一个级联查询
当我们想关联两个数据类,它们有共同的属性,这时候就可以用级联查询
第五种,用lamb表达式来写级联查询
这里的selectMany会把两个数据类联合起来,和用from是一个道理,然后在where里面书写方法逻辑即可
var res =master.SelectMany(m => kongfu, (m, k) => new { master = m, kongfu = k }).Where(x =>x.master.Kungfu ==x.kongfu.Name && x.kongfu.Power > 90);
//var res = master.Where(m=>m.Level>9);
foreach (var s in res)
{
Console.WriteLine(s);
}
Console.ReadKey();
var res = from m in master from k in kongfu
where m.Kungfu==k.Name&&k.Power>96
select m;
//var res = master.Where(m=>m.Level>9);
foreach (var s in res)
{
Console.WriteLine(s);
}
Console.ReadKey();
var res =master.Where(m=>m.Level>9);
var res =master.Where(Test1);
foreach (var s in res)
{
Console.WriteLine(s);
}
Console.ReadKey();
}
static bool Test1(MartialArtsMaster master)
{
if (master.Level > 9 && master.Menpai == "丐帮") return true;
else return false;
}
usingSystem.Collections.Generic;
using System.Linq;
using System.Text;
usingSystem.Threading.Tasks;
namespace LinQ
{
class Program
{
static void Main(string[] args)
{//初始化武林高手
var master = new List<MartialArtsMaster>(){
new MartialArtsMaster(){ Id = 1, Name = "黄蓉", Age =18, Menpai = "丐帮", Kungfu = "打狗棒法", Level = 9 },
new MartialArtsMaster(){ Id = 2, Name = "洪七公", Age = 70,Menpai = "丐帮", Kungfu = "打狗棒法", Level = 10 },
new MartialArtsMaster(){ Id = 3, Name = "郭靖", Age =22, Menpai = "丐帮", Kungfu = "降龙十八掌",Level = 10 },
new MartialArtsMaster(){ Id = 4, Name = "任我行", Age = 50,Menpai = "明教", Kungfu = "葵花宝典", Level = 1 },
new MartialArtsMaster(){ Id = 5, Name = "东方不败",Age = 35, Menpai = "明教", Kungfu = "葵花宝典", Level = 10 },
new MartialArtsMaster(){ Id = 6, Name = "林平之", Age = 23,Menpai = "华山", Kungfu = "葵花宝典", Level = 7 },
new MartialArtsMaster(){ Id = 7, Name = "岳不群", Age = 50,Menpai = "华山", Kungfu = "葵花宝典", Level = 8 },
new MartialArtsMaster() { Id = 8, Name = "令狐冲", Age = 23, Menpai = "华山", Kungfu = "独孤九剑", Level = 10 },
new MartialArtsMaster() { Id = 9, Name = "梅超风", Age = 23, Menpai ="桃花岛", Kungfu = "九阴真经", Level = 8 },
new MartialArtsMaster() { Id =10, Name = "黄药师", Age = 23, Menpai ="梅花岛", Kungfu = "弹指神通", Level = 10 },
new MartialArtsMaster() { Id = 11, Name ="风清扬", Age = 23, Menpai ="华山", Kungfu = "独孤九剑", Level = 10 }
};
//初始化武学
var kongfu = new List<Kungfu>(){
new Kungfu(){Id=1, Name="打狗棒法", Power=90},
new Kungfu(){Id=2, Name="降龙十八掌", Power=95},
new Kungfu(){Id=3, Name="葵花宝典", Power=100},
new Kungfu() { Id= 4, Name ="独孤九剑", Power = 100 },
new Kungfu() { Id = 5, Name = "九阴真经", Power = 100 },
new Kungfu() { Id = 6, Name = "弹指神通", Power = 100 }
};
//var res = from m in master
// where m.Level > 8
// select m;
var res = master.Where(Test1);
foreach (var s in res)
{
Console.WriteLine(s);
}
Console.ReadKey();
}
static bool Test1(MartialArtsMaster master)
{
if (master.Level > 9 && master.Menpai == "丐帮") return true;
else return false;
}
}
}
using System;
usingSystem.Collections.Generic;
using System.Linq;
using System.Text;
usingSystem.Threading.Tasks;
namespace LinQ
{
class Kungfu
{
public int Id;
public string Name;
public int Power;
}
}
using System;
usingSystem.Collections.Generic;
using System.Linq;
using System.Text;
usingSystem.Threading.Tasks;
namespace LinQ
{
class MartialArtsMaster
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Kungfu { get; set; }
public string Menpai { get; set; }
public int Level { get; set; }
public override string ToString()
{
return String.Format("Id:{0},Name:{1},Age:{2},Kungfu:{3},MenPai{4},Level:{5}", Id, Name, Age,Kungfu, Menpai, Level);
}
}
}
023-LinQ中的排
这里介绍两种排序的写法
1. 正常排序,我们用orderBY加条件即可升序排序,倒序的话要在末尾加上descending
2. 用Lamb表达式,在之前案例后面加上.orderBy即可,如果想要继续追加排序条件,就要加上thenBy
var res =master.SelectMany(m => kongfu, (m, k) =>
new { master = m, kongfu = k }).Where(x =>x.master.Kungfu ==x.kongfu.Name && x.kongfu.Power >90).OrderBy(m=>m.master.Age).ThenBy(m=>m.master.Level);
var res = from m in master
from k in kongfu
where m.Kungfu == k.Name && k.Power > 90
orderby m.Age
select new { master = m, kongfu = k };
024-LinQ中Join ON做集合连接
var res = from m in master
join k in kongfu on m.Kungfu equals k.Name
where k.Power > 90
select new { master = m, Kungfu = k };
025.groupBY进行分组
这里是grops的分组方法,我们先用join on进行筛选,这里我们希望筛选出修炼一门武功的人数,我们就可以用groups.Count进行分组和统计,再输出统计即可
var res = from k in kongfu
join m in master on k.Name equals m.Kungfu
into grops
orderby grops.Count()
select new { kongfu =k,count=grops.Count()};
还有一个自身分组,可以筛选出自身数据的属性
比如这里想要筛选修炼各个门派的人的个数,就可以先将它分组赋值给g,然后再输出g的属性
var res = from m in master
group m by m.Menpai
into g
select new { count = g.Count(), key=g.Key };
026.Any操作符和All操作符
bool res = master.Any(m=> m.Menpai == "华山");
Console.WriteLine(res);
bool res2 = master.All(m => m.Menpai == "梅花岛");
Console.WriteLine(res2);
027. 关于Type对象与反射
这里我们先介绍反射中比较重要的Type属性,type可以获取其他类里的各种公共字段,诸如名称,方法名,公共属性,下面是各种获取方法
namespace Type对象与反射
{
class Program
{
static void Main(string[] args)
{
MyClass my = new MyClass();
Type type = my.GetType();//通过这个就可以获取到当前实例的type
Console.WriteLine(type.Name);
Console.WriteLine(type.Namespace);
FieldInfo[] array = type.GetFields();//获得当前类的所有公共字段
PropertyInfo[] array2 = type.GetProperties();//返回所有公共属性
MethodInfo[] array3 = type.GetMethods();//返回所有公共方法
}
}
}
028,关于反射中的程序集
我们可以通过GetType获得程序集,每次当我们允许程序的时候,vs就会生成一个exe后缀的文件
namespace Type对象与反射
{
class Program
{
static void Main(string[] args)
{
MyClass my = new MyClass();
Assembly assem = my.GetType().Assembly;
//Console.Write(assem.FullName);
Type[] types = assem.GetTypes();
foreach (var item in types)
{
Console.WriteLine(item);
}
Console.ReadKey();
}
}
}
029 关于Obsolute特性
当我们想要弃用一个方法并且更换一个新方法时候,我们可以加上Obsolute进行修饰,告诉用户这个方法已经被弃用了
030—Conditional特性
我们可以在文件头部定义一个宏#define,然后在我们在调试方法前定义这个Conditional,就可以通过注释掉宏来控制方法的调用
031—自定义特性
我们除了可以调用系统内置特性来处理事务以外,还可以自己定义特性,一般用于获得服务器传递过来的参数
我们自己定义的特性要遵守这些规则
1. 命名结尾是Attribute
2. 继承自System.Attribute
3. 类要用sealed修饰
4. 特性内一般不定义方法
using System;
usingSystem.Collections.Generic;
using System.Linq;
using System.Text;
usingSystem.Threading.Tasks;
namespace 自定义特性
{
[AttributeUsage(AttributeTargets.Class)]//表示特性可以运用到什么位置
sealed class MyTestAttribute:System.Attribute
{
public string name { get; set; }
public string des { get; set; }
public int id { get; set; }
public MyTestAttribute(string descrpition)
{
this.des = descrpition;
}
}
}
2.这样我们就可以在类前面调用我们的特性了,我么可以传递一个参数des,然后通过type去获取到参数,type可以用typeof(类名来获得)
namespace 自定义特性
{
[MyTest("haha")]
class Program
{
static void Ha()
{
}
static void Main(string[] args)
{
Type type = typeof(Program);//typeof可以获得类名的Type对象
Object[] array = type.GetCustomAttributes(false);//获得类里的所有特性
MyTestAttribute test = array[0] as MyTestAttribute;//强转类型可以获得到第0个特性,也就是这里我们定义的MyTest
Console.WriteLine(test.des);
Console.ReadKey();
}
}
}
031.关于线程
关于线程和进程,这里借用一下别人的解释作为阐述
1,计算机的核心是CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运行。
2,如果工厂的电力有限一次只能供给一个车间使用。也就是说一个车间开工的时候,其他车间就必须停工。背后的含义就是。单个CPU一次只能运行一个任务。(多核CPU可以运行多个任务)
3,进程就好比工厂的车间,它代表CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态。
4,一个车间里,可以有很多工人,他们协同完成一个任务。
5,线程就好比车间里的工人。一个进程可以包括多个线程。
6,车间的控件是工人们共享的,比如许多房间是每个工人都可以进出的。这象征一个进程的内存空间是共享的,每个线程都可以使用这些共享空间。
7,进程就好比工厂的车间,它代表CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态。
8,一个防止他人进入的简单方法,就是门口加一把锁(厕所)。先到的人锁上门,后到的人看到上锁,就在门口排队,等锁打开再进去。这就叫"互斥锁"(Mutualexclusion,缩写 Mutex),防止多个线程同时读写某一块内存区域。
9,还有些房间,可以同时容纳n个人,比如厨房。也就是说,如果人数大于n,多出来的人只能在外面等着。这好比某些内存区域,只能供给固定数目的线程使用。
10,这时的解决方法,就是在门口挂n把钥匙。进去的人就取一把钥匙,出来时再把钥匙挂回原处。后到的人发现钥匙架空了,就知道必须在门口排队等着了。这种做法叫做"信号量"(Semaphore),用来保证多个线程不会互相冲突。
不难看出,mutex是semaphore的一种特殊情况(n=1时)。也就是说,完全可以用后者替代前者。但是,因为mutex较为简单,且效率高,所以在必须保证资源独占的情况下,还是采用这种设计。
032.用委托来实现异步线程的进行
这里注意两点
1. 委托中的BeginInvoke来生成新的线程控制发生
2. IasyncResult可以获得到线程的状态
3. Thread.sleep可以控制进程的休眠,这里的话控制的是检测的频率
class Program
{
static int Test(string name,int num)
{
Console.WriteLine("name:" + name + num);
Thread.Sleep(10);//这个是让线程休眠10ms
return 10;
}
static void Main(string[] args)
{
Func<string,int,int> a = Test;//声明一个Test方法的委托
IAsyncResult s= a.BeginInvoke("老王", 10, null, null);//生成两个新的线程,这里IasyncResult是获得异步进程的状态,两个进程基本可以认为是同时发生
Console.WriteLine("main");
while (s.IsCompleted == false)//判断委托是否完成
{
Thread.Sleep(10);
Console.WriteLine(".");//用来控制检测频率
}
int b = a.EndInvoke(s);//获得异步线程的返回值
Console.WriteLine(b);
Console.ReadKey();
}
}
033——用委托回调方法检测线程的结束
1.这里我们使用委托从外部调用了一个方法,而我们如何检测线程已经结束了呢,这里我们可以用
Lambda表达式来做到这一点,我们开启一个线程,如果线程结束就输出回调方法,就可检测是否完成
class Program
{
static int Test(int number,int id)
{
Console.WriteLine("测试" + number);
return number;
}
static void Main(string[] args)
{
Func<int, int, int> a = Test;
a.BeginInvoke(100, 1, ar =>
{
int res = a.EndInvoke(ar);
Console.WriteLine(res + "回调");
}, null);
Console.ReadKey();
}
}
3. 还有一个方法,就是
我们开启一个线程
AsyncResultar=a.BeginInvoke();之后
Ar.AsyncWaitHandle(time)你传递一个等待时间,它会在线程结束后执行这个方法,如果线程执行超出等待时间,就会返回false,没超出等待时间就结束的话会返回一个true,我们可以通过判断Bool值知道线程是否结束
033——通过Thread开启一个线程
{
class Program
{
static void Test(Object name)
{
Console.WriteLine("开始下载"+name);
Thread.Sleep(2000);
Console.WriteLine("下载完成");
}
static void Main(string[] args)
{
//Thread t = new Thread(Test);
//t.Start("lol");
Thread t = new Thread(() =>
{
Console.WriteLine("开始下载" + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(2000);
Console.WriteLine("下载完成");
});
t.Start();
Console.ReadKey();
}
}
}
然后我们给Thread传递参数有两个方法
1.直接构造一个方法,给定一个object类型的参数,然后在start方法里去传递
class Program
{
static void Test(Object name)
{
Console.WriteLine("开始下载"+name);
Thread.Sleep(2000);
Console.WriteLine("下载完成");
}
static void Main(string[] args)
{
//Thread t = new Thread(Test);
//t.Start("lol");
MyThread t = new MyThread("老王", 3);
t.DownLoad();
Console.ReadKey();
}
}
2.在外部构建一个MyThread类,里面提供好方法和参数,然后在program里调用该方法即可
class MyThread
{
private string name;
private int id;
public MyThread(string name1,int id1)
{
this.name = name1;
this.id = id1;
}
public void DownLoad()
{
Console.WriteLine("开始下载"+name+id);
Thread.Sleep(2000);
Console.WriteLine("下载完成");
}
}
}
034 关于线程池的创建
我们创建线程的时候除了临时定义一个以外,还可以利用系统内置的一个线程池,这个线程池提前提供了很多空的线程,比如这里我们提供了一个方法,然后我们就可以用这个ThreadPool.QueueUserWorkItem这个通过传递一个带参数的方法,实现线程的入池
当然这里入池的线程大多为小线程,并且全都是后台线程且没有优先级的说法。
namespace 线程池
{
class Program
{
static void TestThreading(Object s)
{
Console.WriteLine("线程开始" + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(2000);
Console.WriteLine("线程结束");
}
static void Main(string[] args)
{
ThreadPool.QueueUserWorkItem(TestThreading);
ThreadPool.QueueUserWorkItem(TestThreading);
ThreadPool.QueueUserWorkItem(TestThreading);
ThreadPool.QueueUserWorkItem(TestThreading);
ThreadPool.QueueUserWorkItem(TestThreading);
ThreadPool.QueueUserWorkItem(TestThreading);
Console.ReadKey();
}
}
}
035——用任务开启线程
这里介绍两种开启线程的方法
一个是Task传递方法,再用start调用
一个是TaskFactory方法
class Program
{
static void TestThreading()
{
Console.WriteLine("任务开始");
Thread.Sleep(2000);
Console.WriteLine("线程结束");
}
static void Main(string[] args)
{
//Task t = new Task(TestThreading);
//Console.WriteLine("Main");
//t.Start();
TaskFactory tf = new TaskFactory();
Task t = tf.StartNew(TestThreading);//这也是一种利用任务创建线程的方法
Console.ReadKey();
}
}
036,关于连续任务
如果一个任务t1的执行是依赖于另一个任务t2的,那么就需要在这个任务t2执行完毕后才开始执行t1。这个时候我们可以使用连续任务。
staticvoid DoFirst(){
Console.WriteLine("do in task : "+Task.CurrentId);
Thread.Sleep(3000);
}
staticvoid DoSecond(Task t){
Console.WriteLine("task "+t.Id+"finished.");
Console.WriteLine("this task id is"+Task.CurrentId);
Thread.Sleep(3000);
}
Task t1 =new Task(DoFirst);
Task t2 =t1.ContinueWith(DoSecond);
Task t3 =t1.ContinueWith(DoSecond);
Task t4 =t2.ContinueWith(DoSecond);
Task t5 =t1.ContinueWith(DoError,TaskContinuationOptions.OnlyOnFaulted);
037——用锁来解决多个线程访问同个数据产生调用错误的问题
比如这里,我们会重复调用state,而导致输出state=5,但是本来的逻辑是state=5,+1后就等于6
publicclass StateObject{
private int state = 5;
public void ChangeState(int loop){
if(state==5){
state++;//6
Console.WriteLine("State==5:"+state==5+" Loop:"+loop);//false
}
state = 5;
}
}
staticvoid RaceCondition(object o ){
StateObject state = o as StateObject;
int i = 0;
while(true){
state.ChangeState(i++);
}
}
staticvoid Main(){
var state = new StateObject();
for(int i=0;i<20;i++){
new Task(RaceCondition,state).Start();
}
Thread.Sleep(10000);
}
所以这里我们可以修改一下
向系统申请锁定state,如果被锁定,那么语句就会暂停直到state被申请到,如果没有锁定,那就会锁定它,并且在运行完后解锁
staticvoid RaceCondition(object o ){
StateObject state = o as StateObject;
int i = 0;
while(true){
lock(state){
state.ChangeState(i++);
}
}
}
037——tcp,socket服务器连接
这一块主要看注释学习了,包括了创建socket,申请ip地址和端口号,转换数据类型,传递数据等操作,两个脚本,一个服务器脚本,一个客户端脚本
namespace tcp_socket网络编程
{
class Program
{
static void Main(string[] args)
//1.创建socket
{ //这里包含的是限制了ip地址是ipv4的地址,数据以流的形式传输,传输协议是tcp
Socket t = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//2.绑定ip地址和端口号
IPAddress ipAddress=new IPAddress(new byte[]{127,0,0,1 });//指定ip地址
EndPoint endPoint = new IPEndPoint(ipAddress,7788);//给对应的ip地址指定端口号
t.Bind(endPoint);//给socket绑定对应的端口号
//开始监听
Console.WriteLine("开始监听");
t.Listen(100);//这里表示监听的个数
Socket clientSocket = t.Accept();//暂停当前线程,直到有新的客户端加入的时候,执行下面的代码
//客户端开始和服务器端开始通信
string message = "Hello World";
Console.WriteLine("向客户端发送一条数据");
byte[] data= Encoding.UTF8.GetBytes(message);//这里可以将给定的字符串转换成utf_8中的bytes编码格式
clientSocket.Send(data);
byte[] data2 = new byte[1024];
int length = t.Receive(data2);
string message2 = Encoding.UTF8.GetString(data2,0,length);
Console.WriteLine("收到数据" + message2);
Console.ReadKey();
}
}
}
class Program
{
static void Main(string[] args)
{ //创建socket
Socket t2 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ipaddress = IPAddress.Parse("127.0.0.1");//将字符串转变成ip地址
EndPoint point = new IPEndPoint(ipaddress, 7788);
t2.Connect(point);//和远程主机建立连接
byte[] data = new byte[1024];
int length = t2.Receive(data);//返回收到的数据的长度
string message=Encoding.UTF8.GetString(data, 0, length);//将收到的数据进行解码
Console.WriteLine(message);
Console.WriteLine("给服务器传递数据");
string message2 = Console.ReadLine();
t2.Send(Encoding.UTF8.GetBytes(message2));
Console.ReadKey();
}
}
}
038-udp客户端连接
我们都知道Udp是一种无连接不可靠的协议,很适合用来作为消息的传递,当我们向服务器发送消息时,系统会自动为我们分配端口号和ip地址,接下来,我会展示怎么用udp进行信息传输
1. 服务器端
这里首先我们创建socket,绑定ip地址和端口号,然后创建一个静态方法ReceiveMessage用来接受客服端发送的消息,这里就是用udp里的receiveFrom接受数据了,然后在Main函数里开启一个线程去执行该方法即可
class Program
{
static Socket udpSocket;
static void Main(string[] args)
{
//创建socket
udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
//绑定Id地址和端口号
udpSocket.Bind(new IPEndPoint(IPAddress.Parse("192.168.0.106"),7788));
//建立连接
new Thread(ReceiveMessage) { IsBackground=true }.Start();//开启线程并且把线程设置为后台线程
Console.ReadKey();
}
static void ReceiveMessage()
{
while (true)
{
EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);//用来存储ip地址和端口号
byte[] data = new byte[1024];
int length = udpSocket.ReceiveFrom(data, ref remoteEndPoint);
string message = Encoding.UTF8.GetString(data, 0, length);
Console.WriteLine("从" + (remoteEndPoint as IPEndPoint).Address.ToString() + " " +(remoteEndPoint as IPEndPoint).Port.ToString() + "获得数据" + message);
}
}
}
2. 客户端
这里同样也是创建socket,然后向指定的端口号发送数据即可,比较简单
class Program
{
static void Main(string[] args)
{ //创建socket
Socket udpClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
//向对应ip发送数据
while (true)
{
string message = Console.ReadLine();
byte[] data = Encoding.UTF8.GetBytes(message);
EndPoint ep = new IPEndPoint(IPAddress.Parse("192.168.0.106"), 7788);
udpClient.SendTo(data, ep);
}
Console.ReadKey();
}
}
039——tcpListener
我们建立tcp连接还可以通过tcpListener进行连接
static void Main(string[] args)
{
//tcplistener封装了可以创建socket的方法
TcpListener listener = new TcpListener(IPAddress.Parse("192.168.0.106"),7788);
//开始监听
listener.Start();
//和客户端建立连接
TcpClient client= listener.AcceptTcpClient();
//从客户端那里接受数据
NetworkStream stream= client.GetStream();
byte[] data = new byte[1024];
//这个read里的参数分别表达,从什么地方读取数据,从什么索引开始读取,每次读取最大字节数
//循环读取数据
while (true)
{
int length = stream.Read(data, 0, 1024);
string message = Encoding.UTF8.GetString(data, 0, length);
Console.WriteLine(message);
}
stream.Close();
client.Close();
}
这里是客户端,直接新建TcpClient就可以建立连接了,很方便,然后也是通过数据流来读取数据
class Program
{
static void Main(string[] args)
{
TcpClient client = new TcpClient("192.168.0.106",7788);
NetworkStream stream = client.GetStream();
Console.WriteLine("请输入数据");
while (true)
{
string message = Console.ReadLine();
byte[] data = Encoding.UTF8.GetBytes(message);
stream.Write(data, 0,data.Length);
}
stream.Close();
}
}
040udp连接
Udp连接相比于tcp要简单一点,服务器和客户端区别不大,都可以又做服务器又做客户端,这一块学习主要看代码
1.服务器端,这里接受数据我们选择临时给客户端分配ip地址和端口号,给客户端绑定好ip地址和端口号后就可以接受数据了,用while死循环实现循环接受数据
class Program
{
static void Main(string[] args)
{
//创建udpClient
UdpClient client = new UdpClient(new IPEndPoint(IPAddress.Parse("192.168.0.106"), 7788));
//接受数据
IPEndPoint point = new IPEndPoint(IPAddress.Any, 0);
while (true)
{
byte[] data = client.Receive(ref point);//通过point确认数据来自哪个ip端口号的哪个ip地址
string message = Encoding.UTF8.GetString(data);
Console.WriteLine("收到消息" + message);
}
}
}
2.客户端
class Program
{
static void Main(string[] args)
{
UdpClient client = new UdpClient();
while (true)
{
string message = Console.ReadLine();
byte[] data = Encoding.UTF8.GetBytes(message);
client.Send(data,data.Length,new IPEndPoint(IPAddress.Parse("192.168.0.106"),7788));
}
}
}
041关于fileInfo的一些操作
fileInfo可以用来查看文件属性,这里介绍它的一些操作
class Program
{
static void Main(string[] args)
{
FileInfo fileInfo = new FileInfo("wenxue.txt");
if (fileInfo.Exists == false)
{
fileInfo.Create();//创建文件
}
Console.WriteLine(fileInfo.Directory);//输出文件路径
fileInfo.MoveTo("wenxue2.txt");//文件重命名
Console.WriteLine(fileInfo.Name);//输出文件名字
fileInfo.CopyTo("wenxue3.txt");//复制现有文件但是不允许覆盖
fileInfo.Delete();//删除文件
Console.ReadKey();
}
}
041_关于file和DirectoryInfo中的一些操作
对于FileInfo和DirectoryInfo类中的很多方法也可以使用File和Directory中的很多方法实现。
1,完成一个文件的拷贝
FileInfomyFile = new FileInfo(@"c:\pxx\xx\xxx\xxx.txt");
myFile.CopyTo(@"d:\xx\xx.txt");//拷贝文件
对应的File处理方式
File.Copy(@"c:\xxx\xx\xx\xx.txt",@"d:\xx\xx\xx.txt");
2,判断一个文件夹是否存在
DirectoryInfomyFolder = new DirectoryInfo(@"c:\program files");
myFolder.Exists
对于FileInfo,或者DirectoryInfo进行构造的时候,如果传递了一个不存在的文件或者文件夹路径,这个时候不会出现异常,只有当你使用这个文件或者文件夹的时候才会出现问题。
FileInfo和DirectoryInfo的对象都可以通过Exists属性判断这个文件或者文件夹是否存在
。
042_xml文档的创建和解析
XML 被设计用来传输和存储数据。XML 被设计用来结构化、存储以及传输信息。比如我们创建一个技能信息列表,就可以用xml文件来解析读取
XML 文档必须包含根元素。该元素是所有其他元素的父元素。
XML 文档中的元素形成了一棵文档树。这棵树从根部开始,并扩展到树的最底端。
所有元素均可拥有子元素:
<root>
<child>
<subchild>.....</subchild>
</child>
<child>
<subchild>.....</subchild>
</child>
</root>
这里,我们先创建一个xml文件,这里包含了一些技能信息
<skills>
<skill>
<id>2</id>
<name lang="cn">横扫八荒</name>
<damage>80</damage>
</skill>
<skill>
<id>3</id>
<name lang="cn">棍扫天下</name>
<damage>27</damage>
</skill>
<skill>
<id>4</id>
<name lang="en">bigfireball</name>
<damage>89</damage>
</skill>
</skills>
043_xml解析案例
这里是xml文档,包含了各种属性
这里没有什么好过多介绍的,基本和上个案例相同,就是多一个知识点,获得Attributes属性里有一个索引就是XmlNode[“string name”],这个可以直接检索节点
<SkillInfo>
<SkillList>
<Skill
SkillID="20002"
SkillEngName="Smash"
TriggerType="1"
ImageFile="data/gfx/image/gui_icon_skill_000.dds"
AvailableRace="7"
>
<Name>重击</Name>
</Skill>
<Skill
SkillID="20003"
SkillEngName="Hide"
TriggerType="2"
ImageFile="data/gfx/image/gui_icon_skill_001.dds"
AvailableRace="1"
>
<Name>隐身</Name>
</Skill>
<Skill
SkillID="20004"
SkillEngName="Ikari"
TriggerType="3"
ImageFile="data/gfx/image/gui_icon_skill_002.dds"
AvailableRace="1"
>
<Name>怒之翼</Name>
</Skill>
<Skill
SkillID="20005"
SkillEngName="Revenge"
TriggerType="5"
ImageFile="data/gfx/image/gui_icon_skill_003.dds"
AvailableRace="2"
>
<Name>光之复仇</Name>
</Skill>
</SkillList>
</SkillInfo>
Skill文件
class Skill
{
public int SkillId { get; set; }
public string SkillName { get; set; }
public string Name { get; set; }
public int TriggerType { get; set; }
public string ImageFile { get; set; }
public int AvaiableRace { get; set; }
public override string ToString()
{
return string.Format("SkillId:{0},SkillName:{1},Name:{2},TriggerType: {3},ImageFile:{4},AvaiableRace{5}",SkillId,SkillName,Name,TriggerType,ImageFile,AvaiableRace);
}
}
class Program
{
static void Main(string[] args)
{
List<Skill> skillList = new List<Skill>();
//实例化
XmlDocument document = new XmlDocument();
document.Load("xmlskill.txt");
XmlNode skillnode= document.FirstChild.FirstChild;
XmlNodeList skillnodeList = skillnode.ChildNodes;
foreach (XmlNode fileNode in skillnodeList)
{
Skill skill = new Skill();
skill.Name = fileNode["Name"].InnerText;
XmlAttributeCollection col = fileNode.Attributes;//获取所有子节点的特性
skill.SkillId = Int32.Parse(col["SkillID"].Value);
skill.SkillName = col["SkillEngName"].Value;
skill.TriggerType = Int32.Parse(col["TriggerType"].Value);
skill.ImageFile = col["ImageFile"].Value;
skill.AvaiableRace = Int32.Parse(col["AvailableRace"].Value);
skillList.Add(skill);
}
foreach (Skill s in skillList)
{
Console.WriteLine(s);
}
Console.ReadKey();
}
}
044.关于Json文件的解析
这里我们来看一下json文件是如何进行解析了,首先我们要去官网下载litJson并且在项目中进行引用,接下来,就是解析的过程了
这里是json文本,包含了一些技能信息
[
{"id":2,"name":"天下无双","damage":100},
{"id":3,"name":"天下无贼","damage":150},
{"id":4,"name":"八荒六合唯我独尊功","damage":200}
]
这里是Skill类,存储技能信息并且将其格式化输出
{
class Skill
{
public string name { get; set; }
public int id { get; set; }
public int damage { get; set; }
public override string ToString()
{
return string.Format("id:{0},name:{1},damage:{2}",id,name,damage);
}
}
}
这里是解析文件,
首先1.我们用jsonMapper下的ToObject方法找到文件
2我们用json里的索引器data[]找到各个属性
3. 遍历将各个属性添加道skill数组
4. 遍历输出检测是否解析完成
namespace json学习
{
class Program
{
static void Main(string[] args)
{
List<Skill> skillList = new List<Skill>();
JsonData jsondata = JsonMapper.ToObject(File.ReadAllText("json技能信息.txt"));
foreach (JsonData data in jsondata)
{
Skill skill = new Skill();
JsonData idValue = data["id"];
JsonData nameValue = data["name"];
JsonData damageValue = data["damage"];
int id = Int32.Parse(idValue.ToString());
int damage = Int32.Parse(damageValue.ToString());
skill.id = id;
skill.damage = damage;
skill.name =nameValue.ToString();
skillList.Add(skill);
}
foreach (Skill s in skillList)
{
Console.WriteLine(s);
}
Console.ReadKey();
}
}
}
045—泛型解析json文件和读取json文件
这里是直接给ToObject添加类型,就可以直接读取到上面提到的技能列表的所有信息,然后直接泛型遍历输出即可,非常方便
泛型遍历json文件
List<Skill> skillList = new List<Skill>();
skillList = JsonMapper.ToObject<List<Skill>>(File.ReadAllText("json技能信息.txt"));
foreach (var s in skillList)
{
Console.WriteLine(s);
}
然后我们举一个我们解析主角信息的例子
这里是一个主角信息的json文件,包含了id,name,age和技能列表
{
"name":"小白",
"id": 3,
"age": 17,
"skilllist":
[
{
"id": 2,
"name": "天下无双",
"damage": 100
},
{
"id": 3,
"name": "天下无贼",
"damage": 150
},
{
"id": 4,
"name": "八荒六合唯我独尊功",
"damage": 200
}
]
然后这里是一个Player类,注意这里Player类中的属性命名一定要和json文件的属性命名一样,不然的话无法遍历到
class Player
{
public string name { get; set; }
public int qq { get; set; }
public int age { get; set; }
public List<Skill> skilllist { get; set; }
public override string ToString()
{
return string.Format("name:{0},id:{1},age:{2},skilllist:{3}", name, qq, age,skilllist);
}
}
然后Program类里我们想要解析json文件就非常简单了,加上Player类的泛型,然后直接输出,就可以遍历属性,这里skill是一个集合,不能直接输出,我们再遍历skillList即可查到解析后的信息
Player p = JsonMapper.ToObject<Player>(File.ReadAllText("主角信息.txt"));
Console.WriteLine(p);
foreach (var s in p.skilllist)
{
Console.WriteLine(s);
}
然后我们也可以在Program里写Player的属性然后转换成json文件
Player p = new Player();
p.name = "小王";
p.qq = 234;
p.age = 33;
string json = JsonMapper.ToJson(p);
Console.WriteLine(json);