我的C#学习笔记(六)委托和XML

作者 Zhendong Ho 日期 2018-10-14
C#
我的C#学习笔记(六)委托和XML

单例模式

抛砖引玉

例如QQ应用程序,每次打开QQ,则创建新的登陆窗口,可以登录多个账号。而FeiQ,每次打开都是相同的窗口。

含义

一个类只允许创建一个对象。

步骤

  1. 将构造函数私有化
  2. 提供一个静态方法,返回一个对象
  3. 创建一个单例

Form2类代码

public partial class Form2 : Form
{
//全局唯一的单例
public static Form2 FrmSingle = null;

//构造函数私有化
private Form2()
{
InitializeComponent();
}

//静态方法,返回单例对象
public static Form2 GetSingle()
{
if(FrmSingle == null)
{
FrmSingle = new Form2();
}
return FrmSingle;
}
}

创建单例代码

private void button1_Click(object sender, EventArgs e)
{
Form2 frm2 = Form2.GetSingle();
frm2.Show();
}

XML

概念

xml:可扩展标记语言,用于存储数据。

节点:一个个的标签称为节点。

元素:XML文档中的所有内容。

注意

  1. XML严格区分大小写
  2. XML标签是成对出现的
  3. XML文档有且仅有一个根节点

通过代码来创建XML文档

//通过代码来创建XML文档
//1、引用命名空间
//2、创建XML文档对象
XmlDocument doc = new XmlDocument();
//3、创建第一行描述信息,并且添加到doc文档中
XmlDeclaration dec = doc.CreateXmlDeclaration("1.0", "utf-8", null);
doc.AppendChild(dec);
//4、创建根节点
XmlElement books = doc.CreateElement("Books");
//将根节点添加到文档中
doc.AppendChild(books);

//5、给根节点Books创建子节点
XmlElement book1 = doc.CreateElement("Book");
//将Book添加到根节点
books.AppendChild(book1);

//6、给Book添加子节点
XmlElement name1 = doc.CreateElement("Name");
name1.InnerText = "金瓶梅";
book1.AppendChild(name1);

XmlElement price1 = doc.CreateElement("Price");
price1.InnerText = "10";
book1.AppendChild(price1);

XmlElement des1 = doc.CreateElement("Des");
des1.InnerText = "好看";
book1.AppendChild(des1);

//保存XML
doc.Save("Books.xml");
Console.WriteLine("保存成功");
Console.ReadKey();

创建带属性的XML文档

XmlDocument doc = new XmlDocument();
XmlDeclaration dec = doc.CreateXmlDeclaration("1.0", "utf-8", null);
doc.AppendChild(dec);

XmlElement order = doc.CreateElement("Order");
doc.AppendChild(order);

XmlElement customerName = doc.CreateElement("CustomerName");
customerName.InnerText = "张三";
order.AppendChild(customerName);

XmlElement customerNumber = doc.CreateElement("CustomerNumber");
customerNumber.InnerText = "1000001";
order.AppendChild(customerNumber);

XmlElement items = doc.CreateElement("Items");
order.AppendChild(items);

XmlElement orderItem1 = doc.CreateElement("OrderItem");
//给节点添加属性
orderItem1.SetAttribute("Name", "姓名1");
orderItem1.SetAttribute("Count", "10");
items.AppendChild(orderItem1);

doc.Save("Order.xml");
Console.WtiteLine("保存成功");
Console.ReadKey();

注意

InnerXml和InnerText的区别。添加标签时使用InnerXml,如customerName.InnerXml = “<p>我是一个p标签</p>”。添加文本时使用InnerText,如customerNumber.InnerText = “100001”。

追加XML

//追加XML文档
XmlDocument doc = new XmlDocument();
if(File.Exist("Books.xml"))
{
//如果文件存在,加载XML
doc.Load("Books.xml");
//获得文件的根节点
XmlElement books = doc.DocumentElement;
}
else
{
//如果文件不存在
//创建第一行
XmlDeclaration dec = doc.CreateXmlDeclaration("1.0", "utf-8", null);
doc.AppendChild(dec);
//创建根节点
XmlElement books = doc.CreateElement("Books");
doc.AppendChild(books);
}

//5、给根节点Books创建子节点
XmlElement book1 = doc.CreateElement("Book");
//将Book添加到根节点
books.AppendChild(book1);

//6、给Book添加子节点
XmlElement name1 = doc.CreateElement("Name");
name1.InnerText = "C#开发大全";
book1.AppendChild(name1);

XmlElement price1 = doc.CreateElement("Price");
price1.InnerText = "110";
book1.AppendChild(price1);

XmlElement des1 = doc.CreateElement("Des");
des1.InnerText = "看不懂";
book1.AppendChild(des1);

//保存XML
doc.Save("Books.xml");
Console.WriteLine("保存成功");
Console.ReadKey();

读取XML文档

XmlDocument doc = new XmlDocument();
//加载要读取的XML
doc.Load("Books.xml");

//获取根节点
XmlElement books = doc.DocumentElement;

//获得子节点,返回节点的集合
XmlNodeList xnl = books.ChildNodes;

foreach (XmlNode item in xnl)
{
Console.WriteLine(item.InnerText);
}
Console.ReadKey();

读取带属性的XMl文档

//Xpath
XmlDocument doc = new XmlDocument();
doc.Load("Order.xml");
XmlNodeList xnl = doc.SelectNodes("/Order/Items/OrderItem");//选择定位到该节点下的所有的节点

foreach (XmlNode item in xnl)
{
Console.WriteLine(node.Attribute["Name"].Value);
Console.WriteLine(node.Attribute["Count"].Value);
}
Console.ReadKey();

删除节点

XmlDocument doc = new XmlDocument();
doc.Load("Order.xml");

XmlNode xn = doc.SelectSingleNode("/Order/Items");//选择定位到该节点的单一节点
xn.RemoveAll();
doc.Save("Order.xml");
Console.WriteLine("删除成功");
Console.ReadKey();

委托

为什么要使用委托

将一个方法作为参数传递给另一个方法。

概念

声明一个委托指向一个函数,委托所指向的函数必须跟委托具有相同的签名(返回值和参数)。

委托语法

声明一个委托

//声明一个委托类型
public delegate void DelSayHi(string name);

调用委托

//创建一个委托对象,并指向一个函数
DelSayHi del = new DelSayHi(SayHiChinese);
//调用委托
del("张三");

注意:DelSayHi del = new DelSayHi(SayHiChinese)可以替换为DelSayHi del = SayHiChinese。

将委托作为参数传递给一个方法

public delegate void DelSayHi(string name);

static void Main(string[] args)
{
Test("张三", SayHiChinese);//将委托(函数)传递给方法
Test("Zhangsan", SayHiEnglish);//将委托(函数)传递给方法
Console.ReadKey();
}

public static void Test(string name, DelSayHi del)
{
//调用
del(name);
}

public static void SayHiChinese(string name)//被指向的方法签名与委托相同
{
Console.WriteLine("你好" + name);
}

public static void SayHiEnglish(string name)//被指向的方法签名与委托相同
{
Console.WriteLine("Nice to meet you " + name);
}

匿名函数

概念

没有名称,只有方法体,通常只调用一次。

语法

//声明一个委托
public delegate void DelSayHi(string name);

static void main(string[] args)
{
DelSayHi del = delegate (string name)//匿名函数
{
Console.WriteLine("你好" + name);
};
del("张三");
Console.ReadKey();
}

泛型委托

使用泛型委托,实现求任意类型数组的最大值。

声明泛型委托

//声明一个泛型委托
public delegate int DelCompare<T>(T t1, T t2);

Main方法

static void Main(string[] args)
{
//整型数组
int[] nums = { 1, 2, 3, 4, 5 };
int max = GetMax<int>(nums, Compare1);//委托作为参数
Console.WriteLine(max);

//字符串数组
string[] names = { "sdaf", "fgklbhjkjbckbh", "fds" };
string max1 = GetMax<string>(names, (string s1, string s2) =>//lambda表达式作为参数
{
return s1.Length - s2.Length;
});
Console.WriteLine(max1);
Console.ReadKey();
}

泛型委托求数组最大值

/// <summary>
/// 求泛型数组的最大值
/// </summary>
/// <typeparam name="T">返回类型</typeparam>
/// <param name="nums">泛型数组</param>
/// <param name="del">泛型委托</param>
/// <returns></returns>
public static T GetMax<T>(T[] nums, DelCompare<T> del)//
{
T max = nums[0];
for (int i = 0; i < nums.Length; i++)
{
//要传一个比较的方法
if (del(max, nums[i]) < 0)
{
max = nums[i];
}
}
return max;
}

委托指向的函数

/// <summary>
/// 委托指向的比较整型函数
/// </summary>
/// <param name="n1">第一个整数</param>
/// <param name="n2">第二个整数</param>
/// <returns></returns>
public static int Compare1(int n1, int n2)
{
return n1 - n2;
}

lambda表达式

lambda表达式的本质是匿名函数,lambda表达式比匿名函数更加简单。

定义三个签名不同的委托

public delegate void DelOne();
public delegate void DelTwo(string name);
public delegate string DelThree(string name);

分别使用匿名函数和lambda表达式

//DelOne del1 = delegate() { };//委托
DelOne del1 = () => { };//lambda表达式

//DelTwo del2 = delegate(string name) { };//委托
DelTwo del2 = (string name) => { };//lambda表达式

//DelThree del3 = delegate(string name) { return name; };//委托
DelThree del3 = (string name) => { return name; };//lambda表达式
//也可以省略string
//DelThree del3 = name => { return name; };

lambda表达式在需要委托参数的方法中的应用

List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
list.RemoveAll(n => n > 4);//传递一个委托,表示要删除的元素满足的条件
foreach (var item in list)
{
Console.WriteLine(item);
}

多播委托

即委托不仅可以指向一个函数,它可以指向多个函数。

首先声明一个委托

public delegate void DelTest();

委托指向多个函数

static void main(string[] args)
{
DelTest del = T1;
del += T2;
del += T3;//使用+=或-=使委托指向多个函数
del();//T1 T2 T3
Console.ReadKey();
}

public static void T1()
{
Console.WriteLine("我是T1");
}

public static void T2()
{
Console.WriteLine("我是T2");
}

public static void T3()
{
Console.WriteLine("我是T3");
}