l 类型转换
l 重写ToString和Equals
l 委托事件之自定义控件
l 反射之为记事本添加插件
1.类型转换
只有在内存上存在交集的类型之间才能进行隐式转换,兼容类型。
子类型转向父类型,范围小的类型转向范围大的类型。
相比与用 is 做判断, 然后做强制转换的方式,更推荐使用as做子类型父类型之间的转换,因为即使转换失败,其返回的也是null而不会抛异常。同样相比于强制转换,convert类提供的转换方法转换值为null时返回0而不会抛异常。而int.TryParse()方法相比于int.Parse也不会在转换失败时抛异常。
关于涉及类型转换的地方,具体还是根据实际情况来使用,在不能确定返回结果的情况下,为了避免程序出错,使用那些不会抛出异常中断程序的转换方式。
2.重写tostring和equals方法
自定义类中,我们常常通过重写tostring和equals方法来达到需要的输出和比较效果,下面展示了一个重写的例子:
Person p1 = new Person() { Name="王哲"};
Person p2 = new Person() { Name = "芦路" };
Person p3=new Person (){ Name="王哲"};
if (p1.Equals(p2))
{
Console.WriteLine("{0}和{1}是一个人",p1,p2);
}
if (p1.Equals(p3))
{
Console.WriteLine("{0}和{1}是同一个人",p1,p3);
}
Console.ReadKey();
}
}
//自定义类
class Person
{
string name;
public string Name
{
get { return name; }
set { name = value; }
}
//重写tostring方法
public override string ToString()
{
return this.Name.ToString();
}
//重写比较方法
public override bool Equals(object obj)
{
if (obj != null)
{
Person p = obj as Person;
return this.Name.Equals(p.Name);
}
else
{
return false;
}
}
}
3.委托与事件之自定义控件
委托:关键字 delegate。它是一种数据类型,它可以将方法当做变量进行处理,要求该方法的返回值类型和参数列表与委托定义类型相对应。一般将委托写在类中,默认访问修饰符为internal级别。类中的私有方法可以通过委托实现在其他类中调用(回调函数)。另外委托变量可以同时存储多个方法,这种委托又叫多播委托。
Person p=new Person ();
MyDelegate md = new MyDelegate();
//第一个方法赋值给委托变量是用等号,其后都是+=或-=用于增减内部方法
md.GetRun = p.Fly;
md.GetRun += p.Run;
md.GetRun += p.Do;
md.GetRun += Cut;
md.GetRun();
Console.ReadKey();
}
static void Cut()
{
Console.WriteLine("切掉坏东西");
}
}
class Person
{
string name;
public string Name
{
get { return name; }
set { name = value; }
}
public void Fly()
{
Console.WriteLine("会飞的超人");
}
public void Do()
{
Console.WriteLine("劳作的人");
}
public void Run()
{
Console.WriteLine("奔跑的人");
}
}
class MyDelegate
{
DelegateGet getRun;
internal DelegateGet GetRun
{
get { return getRun; }
set { getRun = value; }
}
}
delegate void DelegateGet();
事件:关键字 event。事件是一种特殊的委托对象,它只拥有普通委托对象的部分功能,包括在类外部增加和移除方法,但是它不能直接用等号做赋值(将方法赋给委托变量)操作,而在委托中要求第一次给委托变量赋值时使用等号,之后再次使用等号时将会清空其前面添加的方法。
事件只能通过+=和-=操作添加和移除方法(可以在类外部进行该操作),保护了操作安全,事件无法在类外部调用。事件的本质就是一个私有的委托和add与remove方法。
委托与事件的作用:当不确定方法的具体执行代码时,可以先用一个委托变量来代替方法的调用。(最常见的就是winform程序中的事件)
自定义控件:
通过编写自定义事件,我们可以做一些简单的自定义控件,如下列出一个自定义三点击按钮控件的代码。
1)新建一个类,定义为ThreeTimesClick,在其中写下自定义属性等。在与其同级的命名空间内定义一个委托。如下:
//定义委托,确定参数类型
public delegate void DelegateThreeClick(object sender,ThreeTimesClick e);
//自定义类
public class ThreeTimesClick
{
string name;
public string Name
{
get { return name; }
set { name = value; }
}
}
2)添加用户控件,在该界面添加一个按钮。点击按钮添加事件方法:
public partial class myClick : UserControl
{
public myClick()
{
InitializeComponent();
}
//定义事件
public event DelegateThreeClick ThreeClick;
//计数变量
int count = 0;
/// <summary>
/// 借用系统内置控件完成自定义控件实现
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
count++;
if (count == 3)
{
ThreeClick(this, new ThreeTimesClick() { Name = "三次点击" });
count = 0;
}
}
}
3)将自定义控件添加到form窗体上,同时拖入一个普通的textBox,打开后台设计代码,为事件赋值执行方法,当写道+=号后,编译器会出现智能提示,按下tab键添加对应的执行方法(没有具体的执行过程,跟winform控件一样)。
this.myClick1.ThreeClick += myClick1_ThreeClick;
void myClick1_ThreeClick(object sender, ThreeTimesClick e)
{
textBox1.Text = e.Name;
}
如此,当我们在winform界面点击三次按钮时,文本框将会显示”三次点击”.
从上例的展示中,我们可以看到委托和事件的影子,通过委托和事件,我们可以提前使用不知道具体执行过程的方法,完成对代码的完善。
4.反射的应用之记事本插件功能
在不将那些包含我们需要的类或功能的程序集引入到当前程序集的情况下,我们可以通过反射,获取需要的数据,这得益于公共语言运行库的动态加载机制。反射与接口的结合,使得程序拓展功能(插件的实现)更加容易。下例中将演示一个自定义插件记事本来展现反射的使用。
1)首先为记事本添加对外接口,其内包含两个方法,一个用于在记事本菜单显示插件名称,一个用于让插件作用于记事本的文本框。
public interface Interable
{
//返回的值将用于在记事本菜单中显示
string ShowTitle();
//实现方法将作用于记事本的文本框,参数即为记事本文本框
void ShowForRichBox(System.Windows.Forms.RichTextBox rtb);
}
2)向记事本主程序中添加加载控件代码。(需要先将接口程序集引用)
private void Form1_Load(object sender, EventArgs e)
{
//通过配置文件,设置插件文件读取路径
string path =System.Configuration.ConfigurationSettings.AppSettings["Inter"];
string[] paths = Directory.GetFiles(path);
for (int j = 0; j < paths.Length; j++)
{
//依次读取插件文件
Assembly asm = Assembly.LoadFile(Path.GetFullPath(paths[j]));
//获取该文件中的所有类型
Type[] tps = asm.GetTypes();
Inter.Interable inte;
//获取接口类型
Type tp = typeof(Inter.Interable);
for (int i = 0; i < tps.Length; i++)
{
//确定插件文件是否实现了接口
if (tp.IsAssignableFrom(tps[i]))
{
//创建插件类型实例
object obj = Activator.CreateInstance(tps[i]);
//接口类型变量指向实现它的类型对象
inte = obj as Inter.Interable;
//为工具栏菜单添加插件名称
ToolStripItem tsi = menuimp.DropDownItems.Add(inte.ShowTitle());
//tag为object类型,可以保存任何类型
tsi.Tag = inte;
//点击插件名称事件
tsi.Click += tsi_Click;
}
}
}
}
//插件名称事件对应方法
void tsi_Click(object sender, EventArgs e)
{
//获取事件来源,并提取其中保存的数据
Inter.Interable inte=((ToolStripItem)sender).Tag as Inter.Interable;
//作用于窗体文本框
inte.ShowForRichBox(richTextBox1);
}
配置文件设置文件读取路径(在当前应用程序加载目录下可以找到该文件):
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<appSettings>
<add key="Inter" value="D:\Inter"/> //设定值为插件文件保存路径
</appSettings>
</configuration>
3)编写插件,并将其放入配置文件中指定的路径位置
/// <summary>
/// 设置记事本文本框颜色的插件
/// </summary>
public class SetColor:Inter.Interable
{
//用于作用于窗体文本框
public void ShowForRichBox(RichTextBox rtb)
{
ColorDialog cd = new ColorDialog();
if (cd.ShowDialog() == DialogResult.OK)
{
rtb.ForeColor = cd.Color;
}
}
//返回插件名称
public string ShowTitle()
{
return "设置颜色";
}
}
运行后效果:
如上只是展示了一个最最简单的例子,而笔者真正要阐述和表达式,只是这种思想,利用反射使程序具有更高的拓展性和兼容性。