语法要点
一些基本的编程范式
- 类名尽量有具体的实际意义
- 类名每个单词首字母大写
- 类中成员变量首字母小写,之后的单词的首字母大写,如
alarmClock
- 类中方法变量每个单词首字母大写,如
GetPulse()
- 结构体定义之前加上S,接口定义之前加上I
- 使用
/ /
注释,不使用/* */
注释 - 全局变量定义之前加上g
关于类的权限问题的说明
- 一般情况下,优先使用protected、其次使用private,最后使用public
- 当类可能需要被继承时,优先考虑protected;当类不太有可能需要被继承时,优先考虑private
- 定义类的名称时,一定要先考虑好它的作用以及相应的方法
泛型编程
- 引入背景
- 为了说明方法所有可能的数据类型而需要覆写某种方法或者某种方法集合的时候,会发生代码膨胀
- 为解决这个问题,引入泛型编程的概念
代码如下
函数定义如下
static void SwapTwoNum<T>(ref T val1, ref T val2) { T swap; swap = val1; val1 = val2; val2 = swap; }
在在函数中调用如下
int a = 1, b = 2; double c = 1.0, d = 2.0; SwapTwoNum<int>(ref a,ref b); SwapTwoNum<double>(ref c, ref d);
可以实现对a,b和c,d的值的交换(此处使用引用类型直接改变变量的值)不用对函数进行覆写,直接改变函数后T的类型即可
- 注:泛型不能对2个不同的数据类型进行操作,比如若要定义
Swap<T1,T2>(ref T1 val1 , ref T2 val2)
则在运行时会出错。
Static的说明:
- 函数中的static表示该变量只在第一次进入该函数时会赋给一个初值,以后进入时直接调用上次的值
- 类中静态成员表示不需要实例化就可以直接查看该成员信息,调用方法是:”类名.成员名”。如果不加static关键字,则需要在实例化类之后,再在具体的类之后调用该成员信息
继承:
- 子类可以继承基类的除私有以外的所有成员和方法,想重写基类的方法时,需要首先在基类中用virtual声明方法,再在子类中用override(覆盖)关键字声明该方法
接口(Interface)
注意的问题
- 接口不能包含常量、字段、运算符、实例构造函数、析构函数或类型、不能包含静态成员。
- 接口成员是自动公开的,且不能包含任何访问修饰符。
- 接口自身可从多个接口继承,类和结构可继承多个接口,但接口不能继承类。
包括显式接口和隐式接口
隐式接口
- 如下例,
void ImplicitBark();
是隐式接口,类继承该接口时,需要重新定义该接口public void ImplicitBark()
, 关于调用隐式接口的方法
way 1
IBark ie = new InterfaceExample(); ie.ImplicitBark();
way 2
InterfaceExample ie = new InterfaceExample(); ie.ImplicitBark();
- 如下例,
显式接口
- 需要通过
void IBark.ExplicitBark()
来定义该方法,而且不能用public、protected、private等定义 显示接口的定义方法
way 1
IBark ie = new InterfaceExample(); ie.ExplicitBark();
注:隐式接口的第二种方法在此处不适用,否则会报错。若是想用,需要进行强制类型转换
InterfaceExample ie = new InterfaceExample(); ((IBark)ie).ExplicitBark();
- 需要通过
代码
类和接口定义
public interface IBark { void ImplicitBark(); void ExplicitBark(); } public class InterfaceExample : IBark { public InterfaceExample() { } public void ImplicitBark() { Console.WriteLine(" this is ImplicitBark "); } void IBark.ExplicitBark() { Console.WriteLine(" this is ExplicitBark "); } }
main.c
IBark ie = new InterfaceExample(); ie.ImplicitBark(); ie.ExplicitBark();
隐式接口存在的意义
- 一般情况,当类或者结构要实现的是单个接口,可以使用隐式实现。
- 如果类或者结构继承了多个接口且接口中具有相同名称成员时,就要用到显式实现,当显式实现方式存在时,隐式实现方式就失效了。
系统中常用的静态类
- Math
- DateTime
- Random
字符串相关说明
- StringBuilder比String操作要快得多
- 可以通过格式化控制符进行输出,比如说工资、时间等变量,可以输出为固定的格式,用法为String.Format….
数组
一维数组
定义
- 数据类型[ ] 数组名
int[] a;
初始化
- 静态初始化
- 将所有的元素初始值列举出来
string[] b ={ "篮球", "足球", "排球", "网球", "羽毛球", "乒乓球" };
- 动态初始化
- 先固定数组大小,没有赋初始值
int[] a = new int[10];
遍历访问数组
- foreach
- foreach(类型 标识符 in array)
foreach(int i in ArrayA)
二维数组
定义
int[ , ] a;
初始化
- 静态
int[ , ] a ={ { 3, 5, 7 }, { 2, 4, 6 }, { 9, 5, 1 } };
- 动态
float[ , ] score = new float[7,5];
锯齿状数组
int [][] a = new int[12][];
,该二维数组的每一行的元素个数都可以不相同
创建数组的静态方法
Array arr = Array.CreateInstance(Type type, int[]length, int[]lowbound);
- 只能用SetValue和GetValue获取或者对数组进行赋值
数组的基本属性
- Length:长度
- Rank:维度
- Clone:复制
b = (int[])a.Clone();
:将a复制到b中- 复制之后b的长度被改变,创建一个关于a的浅表副本
- 改变b的值,不改变a的值,即目标数组和源数组是独立的
- CopyTo:复制
a.CopyTo(b,2);
:将a复制到b中,b从b[2]开始改变- 注意要使数组下标符合要求
- 这样赋值之后改变b,不会对a造成影响,即a和b是独立的
关于复制还有一种方法
代码
int[] a = { 1, 2, 3, 4, 5, 6, 7 }; int[] b = a;
- 相当于创建一个关于a的引用,之后改变b中元素的值,a中元素的值也会跟着改变
利用程序运行时间间接测量算法复杂度
- 用DateTime类相减是不准的,因为.NET中还有线程处理以及无用单元收集等其他事情需要处理,测得的时间会和算法复杂度运行时间相差很多
常用以下方法测量算法运行时间(不考虑编译器处理其他事情消耗的时间)
TimeSpan duration; /*algorithm you want to check should be placed here*/ //... /*end of you algorithm*/ duration = Process.GetCurrentProcess().TotalProcessorTime; Console.WriteLine(duration.TotalSeconds);
数据结构
基础的排序算法
- 冒泡排序
- 选择排序
- 插入排序
基础的查找算法
- 顺序查找
- 二叉查找
- 递归的二叉查找算法
栈(Stack)
采用LIFO(先入后出)的结构,主要应用如下
算术运算
进制转换
进制转换函数
//将10进制的数num转化为b进制的数,将转化后的数以栈的形式返回 //栈的顶部是最高位,底部是最低位 static Stack HexConversion(int num, int b) { Stack digits = new Stack(); do { digits.Push(num % b); num /= b; }while( num != 0 ); return digits; }
观察结果的代码如下
Stack stack4Three = new Stack(); stack4Three = HexConversion(100,8); //将100的10进制转化为8进制 while (stack4Three.Count > 0) { Console.Write(stack4Three.Pop()); } Console.WriteLine();
队列(Queue)
常用组件
ImageList
- 可以将一组图片加入到该集合中,之后可以通过下标去访问该图片集合
- 可以用来显示动画(先将一组图片放在集合中,再循环显示即可)
Timer
- 可以实现固定的时间间隔执行某段程序,相当于定时中断
- 常用于定时刷新界面等,如果时间显示
菜单
MenuStrip
ToolStrip
对话框
文件选择对话框
文件操作
文件
这里主要是对文件进行复制、删除、移动、新建等操作
- File
- 静态类,不需要实例化
- FileInfo
- 必须被实例化才能使用
文件夹
- Directory
- 静态类,不需要实例化
- DirectoryInfo
- 必须被实例化才能使用
文件读写
引入流的概念,对文件读或写
流
- 字节序列化的抽象概念,串行化设备的抽象表示
- 留得基本操作
- Read
- Write
- Seek:查找(网络流一般不支持查找)
创建文件流的方法
- FileStream类
写入文件时,需要将字符串转换为字节数组,实现方法如下
string a = "123"; ASCIIEncoding b = new ASCIIEncoding(); byte[] c = b.GetBytes(a); //
之后就可以将字节数组c写入文件中,如果此时a字符串是中文字符,则转换的过程中会出现信息丢失的现象(只保留低8位)
GDI图形图像处理相关
坐标系的概念
GDI有3种坐标系
- 世界坐标系
- 设备坐标系
- 页面坐标系
图形绘制相关技巧
错误的尝试
在窗口中绘制好图像后,改变窗口大小或者最小化、最大化等操作时,绘制的图像会“消失”,要想使图像信息不丢失,需要在窗口中添加Paint事件,每次绘制时,需要重新绘制该图像
private void CClockExercise_Paint(object sender, PaintEventArgs e) { Graphics Pic = CPbDispGraph.CreateGraphics(); Pen MyPen = new Pen(Color.Red); Pic.DrawEllipse(MyPen , 0, 0 , CPbDispGraph.Width - 1, CPbDispGraph.Height - 1); Pic.DrawLine(MyPen , new Point(0, 0) , new Point(CPbDispGraph.Width - 1, CPbDispGraph.Height - 1)); Pic.Dispose(); }
注:这种方法实现重绘时,速度比较慢,有时改变控件大小后,需要较长时间才能看到改变后的效果。
慢的原因
- 在Form的重绘函数中想要重绘PictureBox的窗口,速度就慢下来了,要想加快速度,需要直接在需要重绘的窗口中调用该窗口的重绘函数
改进后的代码如下:
private void CPbDispGraph_Paint(object sender, PaintEventArgs e) { Graphics g = e.Graphics; PointF[] pt = new PointF[360]; //定义点数组 using (Pen pen = new Pen(Color.Black)) //定义画笔 { for (int i = 0; i < 720; i += 2) //计算获得曲线上每个点的坐标 { pt[i / 2].X = i / 2; pt[i / 2].Y = (float)(60 - 50 * Math.Sin(Math.PI / 180 * i)); } g.DrawLine(pen, 0, 60, 400, 60); //绘制一条表示数轴的直线 g.DrawCurve(pen, pt); //绘制基数样条构成的正弦曲线 } }
这段代码是PictureBox的重绘函数
描述坐标系的基本结构
- Point和PointF
- Size和SizeF
- Rectangle和RectangleF
基本的GDI对象
- 画笔(Pen)
- 笔刷(Brush)
关于图像双缓冲的说明
- 图像显示时,会涉及到刷新的问题,窗口上控件太多或者刷新速度太快时,往往会出现图像闪烁的问题,因为程序一直在刷新;而如果等用户画完之后再输出就不会出现这种情况。
- solution:新建一个Bitmap的画布,将所有的东西先画到画布上,再将画布一次性加载到窗口中,可以避免屏幕闪烁的情况
代码
Bitmap bmpBuffer = new Bitmap(CPbRealTimePlot.Width , CPbRealTimePlot.Height); Graphics g = Graphics.FromImage(bmpBuffer); //以下的Using表明pen的作用域,当这段代码执行结束之后,会释放pen资源 using (Pen pen = new Pen(Color.Blue)) //定义画笔 { //数据平移与计算 for (int i = 0; i < NUM_TO_DISP-1; i++) { ptForRealTimeDisp[i].Y = ptForRealTimeDisp[i + 1].Y; } ptForRealTimeDisp[NUM_TO_DISP-1].Y = 50 + 50 * (float)Math.Sin(2*Math.PI* numForSinPlot/NUM_TO_DISP); numForSinPlot++; if (numForSinPlot == NUM_TO_DISP) numForSinPlot = 0; //绘图 g.Clear(Color.White); // 首先清除图像,再进行绘制 g.DrawLine(pen, 0, 50, 400, 50); //绘制一条表示数轴的直线 g.DrawCurve(pen, ptForRealTimeDisp); //绘制基数样条构成的正弦曲线 } //下面这段代码将Bitmap资源释放到窗口中并显示 using (Graphics tg = CPbRealTimePlot.CreateGraphics()) { tg.DrawImage(bmpBuffer, 0, 0); }
函数或者方法数据传递的区别
值传递
在通过值传递作为方法参数的变量时,传递给方法的是数据副本。在方法中对该数据的任何修改都不会对初始值有任何影响。
static void DoSomething(int parameter)
引用传递
如果传递引用的话,那么无论在程序的什么地方作改变的话(可能是在另一个方法、属性中,甚至是另一个对象中),都会改变使用改引用的值。对方法中任何参数的改变都将影响方法的返回值。
static void DoSomething(ref int parameter)
值传递引用类型
传递的是对象的引用
static void DoSomething(Person somePerson)
如果按照如下方法,则不会修改主程序中somePerson的一些属性
static void DoSomething(Person somePerson) { somePeron=new Person(100); //创建一个新对象,赋值会不起作用 }
如果按照以下方法,则会修改主程序中somePerson的一些属性
static void DoSomething(Person somePerson) { somePerson.Age=100; //这个对象相当于一个引用,直接改变其属性即可 }
some other tips
获得服务器的运行时间
Environment.TickCount
:以毫秒为单位 ,一般对应开机时间
窗口大小改变时,窗口内控件大小也相应发生改变
为窗口添加ResizeBegin事件
private void CClockExercise_ResizeBegin(object sender, EventArgs e) { BeforeSize = this.Size;//获取之前的窗口大小 }
为窗口添加ResizeEnd事件
private void CClockExercise_ResizeEnd(object sender, EventArgs e) { Size EndSize = this.Size; float percentWidth; float percentHeight; //获得变化比例 percentWidth = (float)EndSize.Width / BeforeSize.Width; percentHeight = (float)EndSize.Height / BeforeSize.Height; ChangeAllSzie(this,percentWidth,percentHeight);//调用递归函数,改变控件的大小 }
其中,上面的ChangeAllSzie函数是递归的,描述如下
private void ChangeAllSzie(Control control , float percentWidth , float percentHeight) { foreach (Control ctrl in control.Controls) { //按比例改变控件大小 ctrl.Width = (int)(ctrl.Width * percentWidth); ctrl.Height = (int)(ctrl.Height * percentHeight); //为了不使控件之间覆盖 位置也要按比例变化 ctrl.Left = (int)(ctrl.Left * percentWidth); ctrl.Top = (int)(ctrl.Top * percentHeight); if (ctrl.HasChildren)//如果容器中还有子容器,则进行递归 { ChangeAllSzie(ctrl, percentWidth, percentHeight); } } }
注意
- 此时,窗口的FormBorderStyle属性一定要设置为Sizable
- 当最大化或者最小化窗口时,控件大小不发生改变,只有拖动窗口改变其大小时,控件大小才相应随之改变
关于添加事件后,删除事件对应代码,程序运行出错的说明
- 在双击添加事件后,VS做了
- 添加事件响应代码
- 在”窗口名.Designer.cs”中添加了
this.button1.Click += new System.EventHandler(this.button1_Click);
,这通常在#region Windows 窗体设计器生成的代码
中隐藏,需要将其展开
- 如果需要删除对应的事件响应,需要同时删除以上2处才可以
执行CMD命令
- 包含命名空间
using System.Diagnostics;
- 调用
Process.Start("CMD.Exe");
,或者其他命令也可以
CLR(Common Language Runtime)
- 定义:公用语言运行时
- 相当于一个运行环境,C#先编译成中间语言,再在CLR上面执行
Dispose资源释放相关
资源
在CLR出来之后,Windows系统资源开始分为“非托管资源”和“托管资源”。
- 非托管资源是
- 所有的Window内核对象(句柄)都是非托管资源,如对于Stream,数据库连接,GDI+的相关对象,还有Com对象等等,这些资源并不是受到CLR管理;
- 需要显式释放的,也即需要你写代码释放
- 托管资源是
- 由CLR管理分配和释放的资源,即由CLR里new出来的对象。
- 并不需要显式释放,但是如果引用类型本身含有非托管资源,则需要进行现实释放
释放规则
- 新建窗口等资源时,因为受系统管制,资源回收速度较快,因此不需要Dispose回收
GDI绘图中,如果新建画笔、笔刷、图像等对象时,因为比较占用系统资源,因此在函数执行结束,即绘图完成时,需要将其释放,用pen.Dispose()即可。或者采用以下方法:
using (Pen pen = new Pen(Color.Black)) //定义画笔 { for (int i = 0; i < 720; i += 2) //计算获得曲线上每个点的坐标 { pt[i / 2].X = i / 2; pt[i / 2].Y = (float)(60 - 50 * Math.Sin(Math.PI / 180 * i)); } g.DrawLine(pen, 0, 60, 400, 60); //绘制一条表示数轴的直线 g.DrawCurve(pen, pt); //绘制基数样条构成的正弦曲线 }
这是确定了Pen的生命周期,即这段代码执行完之后,Pen资源就会被系统释放。
因为重绘图像时,图像是作为参数传进来的,在结束时,不需要Dispose图像,直接由系统控制其生命周期就行。
关于TextBox限制数字输入的问题
为TextBox添加KeyPress的事件,代码如下
private void CTbSecondSet_KeyPress(object sender, KeyPressEventArgs e) { if ((!Char.IsNumber(e.KeyChar)) && (e.KeyChar != (char)8)) //判断按下的字符是数字或者退格,若都不是,则提示用户输入数字 { e.Handled = true; MessageBox.Show("请输入数字"); } else { e.Handled = false; } }
如需要对数字判断大小,看是否在合理范围内,则需要通过给TextBox添加Leave事件,代码如下
private void CTbHourSet_Leave(object sender, EventArgs e) { if (CTbHourSet.Text.Equals("")) CTbHourSet.Text = "0"; if (Convert.ToInt32(CTbHourSet.Text) < 0 || Convert.ToInt32(CTbHourSet.Text) >= 24) { CTbHourSet.Clear(); MessageBox.Show("请输入有效的时间范围"); } }
获取鼠标在屏幕上的位置
电脑屏幕
Point pt = MousePosition;
,得到鼠标的值,可以通过pt.X
和pt.Y
得到其X、Y的坐标值,原点是电脑屏幕左上角Point pt2= this.PointToClient(pt);
,获得鼠标在窗体上的坐标值,即以窗体的原点为屏幕原点,对pt进行整体平移
关于在工程中考虑问题的一些方法
原谅我还没想好标题
- 对于比较的问题,比如说闹钟到达时间就触发某一事件,则触发条件需要设置得尽量严苛。不能是简单地对时间进行比较就结束,而是应该记录上次的时间,当当前时间大于等于闹钟时间且上次时间小于闹钟时间时,触发闹钟响应事件。即用线与点进行比较。这是因为windows的系统时间为了和网络时间进行校准,有时会发生跳变,有可能恰好跳过闹钟时间,此时就需要采用该种方法对闹钟时间进行判断。
- 在实时数据采集处理过程中,这种“点对线”的思想十分常用,因为离散控制系统中,数据很容易发生跳变,涉及比较时,采用这种思想可靠性会更高。
代码异常处理
- 在写代码时,一定要考虑到各种可能出现的条件,尽可能分情况讨论并给用户提示以便于解决问题,尽可能不用
try...catch...
抛出异常。
指标的关系
- 每个设计任务的指标都是有其一定的背景的,不要认为指标看起来没有什么用就不去管它,否则可能会丢掉很多细节
WPF操作相关
使用dll或者外部库
- 在“解决方案”里右击“引用”,添加dll的引用;
- 在toolbox里右击,单击“选择项”,在wpf组件中选择“浏览”,选中加载的dll,即可在“所有WPF组件”中找到新加的控件了
Silverlight使用相关
安装
- 尼玛,这个花了我2天!!!
- 安装需要版相同的tools和SDK,我当时就是安装了不同版本的,所以程序可以运行但是设计器中观察不了,安装tools时,其实就将runtime也安装了(包含在里面)
- VS2010来说,最好使用3.0或者4.0版本的Siverlight,因为如果要用5.0版本的,需要安装VS补丁包,比较麻烦。
- 先安装SDK,再安装tools
sharepoint相关
在sharepoint中插入webpart部件
有以下几种方法
* 直接新建webpart然后插入xap文件an,不过对于xap的url地址,我不是特别清楚,也没有尝试成功
* 先将silverlight产生的xap文件保存在sharepoint的文档库中,此时xap文件的url地址更加方便一点,找到那个文档,右击复制链接即可
关于实习的过程中silverlight、WPF、WCF通讯相关的问题
通讯简介
- 实习的工程项目是BS架构,通讯采用的是客户端对象模型,因此需要在本地实时地获取sharepoint数据,通过VS导入sharepoint数据。
- 关于通讯,WPF和sharepoint通讯是利用的同步通讯,即而silverlight作为webpart,在网页中进行相关操作时利用的异步通讯,因此在此过程中需要利用异步编程方法。
- 关于webpart的引入文件,即xap文件的放置问题,如果放在本地(服务器需要能够与访问到本地路径才可),网页相关的缓存可能会使刚刚在文件目录里更新的文件得不到及时的引用,即浏览器直接引用浏览器之前的缓存了;因此,选择xap文件放在sharepoint的文档库中,这样因为sharepoint的安全保护问题,会使浏览器对该网页的缓存无效,即每次引入时都是最新的文档,可以保证网页的实时刷新
WPF操作过程中的数据的增删改查相关问题
准备工作
- 在数据源中添加sharepoint数据,需要填写添加网页的网址,VS会自动生成基于sharepoint的引用以及相关的命名空间等。
相关代码
初始化
private CStopCondition stopCondition = new CStopCondition(); private ProTest.温升实验DataContext dc = null; //重点!之后都是对这个对象进行操作
数据连接服务
private bool IsConnect( ) { try { dc = new ProTest.温升实验DataContext( new Uri("http://win-ubfvccleemg/sites/ProTest/_vti_bin/ListData.svc/"));//存储数据的网址 //dc.Credentials = CredentialCache.DefaultCredentials; NetworkCredential nc = new NetworkCredential("jiang", "qwer1234-");//帐号和密码 dc.Credentials = nc; return true; } catch { return false; } }
增加数据项
private bool AddData(CStopCondition cSc) { if (dc.停止条件列表.Where(c => c.名字 == cSc.name).FirstOrDefault() == null) { ProTest.停止条件列表Item pt = new ProTest.停止条件列表Item(); pt.名字 = cSc.name; pt.数据 = cSc.Serialize(); pt.标准 = cSc.referCriterion; pt.描述 = cSc.description; pt.参数 = cSc.parameters; pt.种类 = cSc.kind.ToString(); dc.AddTo停止条件列表(pt); dc.SaveChanges(); return true; } else { return false; } }
修改数据项
private bool ChangeData() { int num = dc.停止条件列表.Where(c => c.名字 == stopCondition.name).Count(); if (num >= 1 && (nameOld != stopCondition.name)) { return false; } else { var item = dc.停止条件列表.Where(c => c.名字 == nameOld).FirstOrDefault(); item.名字 = stopCondition.name; item.数据 = stopCondition.Serialize(); item.标准 = stopCondition.referCriterion; item.描述 = stopCondition.description; item.参数 = stopCondition.parameters; item.种类 = stopCondition.kind.ToString(); dc.UpdateObject(item); dc.SaveChanges(); return true; } }
查询数据项
private bool QueryData(string name) { if (dc.停止条件列表.Where(c => c.名字 == name).FirstOrDefault() != null) { var results = (from item in dc.停止条件列表 where item.名字 == name select new { data = item.数据 }); string str = ""; foreach (var res in results) { str += res.data; } MessageBox.Show(str); return true; } else { return false; } }
删除数据项
private bool DeleteData(string name) { if (dc.停止条件列表.Where(c => c.名字 == name).FirstOrDefault() != null) { dc.DeleteObject(dc.停止条件列表.Where(c => c.名字 == name).FirstOrDefault()); dc.SaveChanges(); return true; } else { return false; } }
silverlight操作sharepoint数据以及将xap部署到sharepoint的webpart中去的问题
网页部署相关问题
- silverlight工程生成解决方案后会在
SilverlightApplication1\SilverlightApplication1.Web\ClientBin
的文件夹中生成xap文件,将此文件放入sharepoint的文档库中,然后在网页中插入webpart,其指定的路径即xap存储对应的路径。也可以放在服务器可以访问到的路径,但此时网页缓存的问题难以解决
相关操作
说明:因为silverlight和sharepoint关联时,是进行异步通信的,对数据的增上改查等操作都需要异步通信编程
* 初始化
private Condition stopCondition = new Condition();
private PageProperty pagePro = PageProperty.IsAdd;//存储网页是新建还是编辑
private ProTest.温升实验DataContext dc = null;
//进入该网页时,会有相关信息,
//包括操作的数据项(如果有的话),以及跳转的原网页等信息,将其存储在字典里
private IDictionary<string, string> webInfo = HtmlPage.Document.QueryString;
* 增加数据
private void AddData(Condition cSc)
{
bool addFail = false;
//异步
try
{
dc.StopCondition02.BeginExecute((result) =>
{//lambda表达式
try
{
var items = dc.StopCondition02.EndExecute(result);
//下面这句话需要尝试一下,因为之前这个查询的任务不能运行
//if (items.Where(c => c.名字 == stopCondition.name) != null)
foreach (var item in items)
{
if (item.名字 == stopCondition.name)
{
addFail = true;
break;
}
}
if(addFail)
{
CTbForDebug.Text += "新建页中已有该名称,无法添加停止条件\n";
}
else
{
ProTest.StopCondition02Item pt = new ProTest.StopCondition02Item();
pt.名字 = cSc.name;
pt.数据 = cSc.Serialize();
pt.标准 = cSc.referCriterion;
pt.描述 = cSc.description;
pt.参数 = cSc.parameters;
pt.种类 = cSc.kind.ToString();
dc.AddToStopCondition02(pt);
dc.BeginSaveChanges((ar) =>
{
if (ar.IsCompleted)
{
CTbForDebug.Text += "添加成功\n";
CloseThisPage();
}
else
{
CTbForDebug.Text += "添加失败\n";
}
}, null);
}
}
catch (Exception ex)
{
CTbForDebug.Text += ex.Message;
}
}, null);
}
catch(Exception ee)
{
MessageBox.Show(ee.ToString());
}
}
* 编辑数据
编辑数据的语句和添加数据类似,只是有以下几个地方需要注意:
* 需要新建一个数据项的对象,当var对象找到时(数据库中对应的数据项),将var类型隐式转换为数据项对象,之后直接修改这个数据项的对象即可
* 修改结束后,需要利用以下方式进行更新
//dc.AttachTo("StopCondition02", (ProTest.StopCondition02Item)forChg);//这句话不加竟然也可以运行成功。。
dc.UpdateObject(forChg);
第一句是将数据和数据项进行关联,然而自己操作的时候发现根本不需要这一句。。boss之前测试说需要,我就先在这里注释掉,不行再加上。。
* 保存数据库
* 保存数据
dc.BeginSaveChanges((ar) =>
{
if (ar.IsCompleted)
{
CTbForDebug.Text += "修改成功\n";
CloseThisPage();
}
else
{
CTbForDebug.Text += "修改失败\n";
}
}, null);
* 注:关于lambda表达式的使用问题
* 当初它的引入是为了方便编程,即省略某些函数的作用
* 现在发现,siverlight操作sharepoint的数据时,我因为不熟,不敢随意改,导致现在的函数都是有多层嵌套的问题,显得特变乱。
* 当嵌套层次比较多时,不建议用lambda表达式,直接用普通的异步编程即可。。然而我并没有学过。。
C#生成二维码
dll文件
- ThoughtWorks.QRCode.dll
- 添加引用之后,需要在程序开始处添加
using ThoughtWorks.QRCode.Codec;
生成二维码函数
代码如下
private void GenerateQR() { //Image image; //image = qrCodeEncoder.Encode(data, Encoding.UTF8); //MemoryStream ms = new MemoryStream(); //image.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp); //byte[] bt = ms.ToArray(); } /// 生成二维码 /// </summary> /// <param name="strData">要生成的文字或者数字,支持中文。如: "4408810820 深圳-广州" 或者:4444444444</param> /// <param name="qrEncoding">三种尺寸:BYTE ,ALPHA_NUMERIC,NUMERIC</param> /// <param name="level">大小:L M Q H</param> /// <param name="version">版本:如 8</param> /// <param name="scale">比例:如 4</param> /// <returns></returns> public void CreateCode_Choose(string strData, string qrEncoding, string level, int version, int scale) { QRCodeEncoder qrCodeEncoder = new QRCodeEncoder(); string encoding = qrEncoding; switch (encoding) { case "Byte": qrCodeEncoder.QRCodeEncodeMode = QRCodeEncoder.ENCODE_MODE.BYTE; break; case "AlphaNumeric": qrCodeEncoder.QRCodeEncodeMode = QRCodeEncoder.ENCODE_MODE.ALPHA_NUMERIC; break; case "Numeric": qrCodeEncoder.QRCodeEncodeMode = QRCodeEncoder.ENCODE_MODE.NUMERIC; break; default: qrCodeEncoder.QRCodeEncodeMode = QRCodeEncoder.ENCODE_MODE.BYTE; break; } qrCodeEncoder.QRCodeScale = scale; qrCodeEncoder.QRCodeVersion = version; switch (level) { case "L": qrCodeEncoder.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.L; break; case "M": qrCodeEncoder.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.M; break; case "Q": qrCodeEncoder.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.Q; break; default: qrCodeEncoder.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.H; break; } //文字生成图片 Image image = qrCodeEncoder.Encode(strData); string filename = DateTime.Now.ToString("yyyymmddhhmmssfff").ToString() + ".jpg"; string filepath = @"./jpg/"; //如果文件夹不存在,则创建 if (!Directory.Exists(filepath)) Directory.CreateDirectory(filepath); FileStream fs = new FileStream(filepath+filename, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write); image.Save(fs, System.Drawing.Imaging.ImageFormat.Jpeg); fs.Close(); image.Dispose(); }
说明
- 使用如下代码即可生成二维码
代码
private string data = "233333"; CreateCode_Choose(data , "BYTE" , "L" , 8 , 4 );//这里的参数均是比较常用的参数
- 注:这种方法适合生成数字或者中英文字符的文本
将图片插入二维码中
代码
public Image GCode(String data) { QRCodeEncoder qrCodeEncoder = new QRCodeEncoder(); qrCodeEncoder.QRCodeEncodeMode = QRCodeEncoder.ENCODE_MODE.BYTE; qrCodeEncoder.QRCodeScale = 5; qrCodeEncoder.QRCodeVersion = 7; qrCodeEncoder.QRCodeErrorCorrect = QRCodeEncoder.ERROR_CORRECTION.L; var pbImg = qrCodeEncoder.Encode(data, System.Text.Encoding.UTF8); var width = pbImg.Width / 10; var dwidth = width * 2; Bitmap bmp = new Bitmap(pbImg.Width + dwidth, pbImg.Height + dwidth); Graphics g = Graphics.FromImage(bmp); var c = System.Drawing.Color.White; g.FillRectangle(new SolidBrush(c), 0, 0, pbImg.Width + dwidth, pbImg.Height + dwidth); g.DrawImage(pbImg, width, width); g.Dispose(); return bmp; } /// <summary> /// 调用此函数后使此两种图片合并,类似相册,有个 /// 背景图,中间贴自己的目标图片 /// </summary> /// <param name="sourceImg">粘贴的源图片</param> /// <param name="destImg">粘贴的目标图片</param> public static System.Drawing.Image CombinImage(System.Drawing.Image imgBack, string destImg) { Image img = System.Drawing.Image.FromFile(destImg); //照片图片 if (img.Height != 50 || img.Width != 50) { img = KiResizeImage(img, 50, 50, 0); } Graphics g = Graphics.FromImage(imgBack); g.DrawImage(imgBack, 0, 0, imgBack.Width, imgBack.Height); //g.DrawImage(imgBack, 0, 0, 相框宽, 相框高); //g.FillRectangle(System.Drawing.Brushes.White, imgBack.Width / 2 - img.Width / 2 - 1, imgBack.Width / 2 - img.Width / 2 - 1,1,1);//相片四周刷一层黑色边框 //g.DrawImage(img, 照片与相框的左边距, 照片与相框的上边距, 照片宽, 照片高); g.DrawImage(img, imgBack.Width / 2 - img.Width / 2, imgBack.Width / 2 - img.Width / 2 , img.Width, img.Height); GC.Collect(); return imgBack;
}
///
/// Resize图片
///
/// 原始Bitmap
/// 新的宽度
/// 新的高度
/// 保留着,暂时未用
/// 处理以后的图片
public static Image KiResizeImage(Image bmp, int newW, int newH, int Mode)
{
try
{
System.Drawing.Image b = new Bitmap(newW, newH);
Graphics g = Graphics.FromImage(b);// 插值算法的质量 g.InterpolationMode = InterpolationMode.HighQualityBicubic; g.DrawImage(bmp, new Rectangle(0, 0, newW, newH), new Rectangle(0, 0, bmp.Width, bmp.Height), GraphicsUnit.Pixel); g.Dispose(); return b; } catch { return null; }
}
运行以下:
string data = "2333"; Image im0 = GCode(data); Image im1 = CombinImage(im0 , "1.jpg");//重点所在 string filename = DateTime.Now.ToString("yyyymmddhhmmssfff").ToString() + ".jpg"; string filepath = @"./jpg/"; //如果文件夹不存在,则创建 if (!Directory.Exists(filepath)) Directory.CreateDirectory(filepath); FileStream fs = new FileStream(filepath + filename, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.Write); im1.Save(fs, System.Drawing.Imaging.ImageFormat.Jpeg); //保存图片 fs.Close(); im0.Dispose(); im1.Dispose();
委托
代码
public delegate string DelegateExam(int num);//在类外进行申明或者里面都行 private void button13_Click(object sender, EventArgs e) { DelegateExam de = new DelegateExam(DecD); Console.WriteLine(de(33));//输出32 Console.WriteLine((new DelegateExam(AddD))(44));//输出45 } private string AddD(int num) { return (num + 1).ToString(); } private string DecD(int num) { return (num - 1).ToString(); }
匿名
Func<string, string> AA = delegate(string name)
{
name += "\nit's Nm";
return name;
};
Console.WriteLine(AA("111"));
没有委托的声明,直接用Func实现(可以查阅资料)
lambda表达式
string mid = " it's middle ";
Func<string, string> lambda = para =>
{
para += "2333";
para += mid;
return para;
};
Console.WriteLine(lambda("GRY"));
lambda是函数名,para是参数,ide可以自动识别它的类型,而且此处Func已经为他指定类型了。=>
左边列出了其参数,右边是返回值,如果很长的话,用大括号括起来,如果不长的话,用以下方法也可以。
Func<double,double,double> Mult = (x,y) => (x*y);
Console.WriteLine(Mult(5,2));
参数用小括号括起来,右边直接写结果即可,不用大括号和return。
IComparer接口的实现
线程传递参数
使用ParameterizedThreadStart(不推荐,因为可以接受任何类型的数据)
//线程类的创建
public class ThreadTest
{
public static void RunTestWithPara(object data)
{
Console.WriteLine("para is {0}",data);
}
}
//按钮响应事件
private void button21_Click(object sender, EventArgs e)
{
Thread[] th = new Thread[10];
for (int i = 0; i < 10; i++)
{
th[i] = new Thread(new ParameterizedThreadStart(ThreadTest.RunTestWithPara));
}
for (int i = 0; i < 10; i++)
{
th[i].Start(i);
}
}
注意:因为是线程启动,所以不是按照严格的递增的顺序输出0~9,每次运行时的输出顺序均不相同。
LINQ表达式与lambda表达式
代码
List<string> list = new List<string>() {"list","ass","efrg","jhgkjs","jnbgt" }; var strWithJ = from a in list where a.StartsWith("j") orderby a ascending select a; foreach (var v in strWithJ) Console.WriteLine(v); Console.WriteLine("---------"); var strA = list.Where((a, index) => a.StartsWith("j") && index % 2 == 0); foreach (var v in strA) Console.WriteLine(v);
注:LINQ表达式是在每次运行foreach进行遍历时才执行搜索过程,因此执行foreach时,操作的是最新的对象。
var dd = from a in list group a by a[0] into g where g.Count() >= 1 select new { c11 = g.Key, cnt = g.Count() }; foreach (var ddd in dd) { Console.WriteLine("{0}---{1}",ddd.c11,ddd.cnt); } list.Add("jasld"); foreach (var ddd in dd) { Console.WriteLine("{0}---{1}", ddd.c11, ddd.cnt); }
group关键词的操作
lookup和dictionary的操作
- lookup只能在一开始就初始化完成,在之后不能再改变,可以是1个Key对应多个Value,可以方便得将数据分组
- Dictionary只能是1个Key对应1个Value,如果冲突的话,会出现异常。
LINQ查询相关
可以实现多级嵌套查询
快捷键设置
当前界面时快捷键
- 直接设置keyup的函数响应即可,但是要使其有效,需要使界面的
KeyPreview
设置为true
。
全局快捷键
- 需要写单独的hotkey函数,这个需要参考网上的其他资料
工作线程
背景
- 在进行一些比较耗时的操作时,希望界面没有卡死,可以通过多线程操作,C#提供了一种比较方便的快捷方式
基本工作过程
- 在界面中添加一个
BackgroundWorker
,需要异步调用它的时候,直接调用bgWorker.RunWorkerAsync(1000);
,1000
是传入的参数,这个会异步调用bgWorker_DoWork
,传入的参数可以通过e.Argument
进行动态转换。 - 可以设置完成时的消息函数,即异步线程在调用结束之后会调用该函数,可以用于更新界面等操作。
- 可以设置是否支持取消该工作线程,如果可以取消,则可以调用
bgWork.CancelAsync();
来取消当前的异步线程。 - 每次异步线程只能处理一个任务,因此如果需要处理多个相同类型的任务时,需要等待工作线程不是
busy
的状态时,才能继续处理下一个任务。
线程池
简介
- 程序可能会运用多个线程来进行操作,加快程序的运行速度,但是一般的多线程操作需要进行等待和唤醒等复杂的实现,十分麻烦,因此
.NET框架
为了简化线程的处理,对每个进程都提供了一个线程池,方便对多个线程进行统一管理,程序可以根据需要来有效地管理线程。 - 线程池是一种多线程处理方式,处理过程中,将任务添加到队列,然后在创建线程后自动启动这些任务。线程池的线程都是后台线程,可以通过设置线程池个数的最大值。超过最大值的其他线程需要排队,等到其他线程完成后才能够启动。
- 线程池中的每个线程都是默认的优先级,使用默认的堆栈大小。
- 线程池比较适合需要执行一些需要多个线程的任务,比如文件的批量处理等,从而提高吞吐量。
不适合使用线程池的环境
- 某一个任务具有特定的优先级。
- 如果具有可能会长时间运行(并因此会阻塞到其他任务)的任务
- 如果需要将线程放到单线程单元中(线程池中的线程均处于多线程单元中)
- 如果需要用永久标识来标识和控制线程,比如想使用专用线程来终止该线程,将其挂起或者按照名称发现它。
C#中的使用
- 有一个专门的ThreadPool类,用于实现线程池
实现方法为
ThreadPool.QueueUserWorkItem(new WaitCallback( Func ), obj); ThreadPool.QueueUserWorkItem(new WaitCallback( Func ));
如果有参数需要传递的话可以用第一种,没有参数传递的话可以用第二种。
正则表达式
匹配8位日期_3位编号
的字符串
if (Regex.IsMatch(str, @"^(\d{8}_\d{3})$"))
{
Console.WriteLine("right...");
}
else
{
Console.WriteLine("wrong...");
}
宏定义
- 参考链接:http://www.runoob.com/csharp/csharp-preprocessor-directives.html
- 注意的地方
- 在文件的最开始定义(using命名空间之前)
手动配置程序集名称
- 需求:有时候需要根据不同的设置生成不同的exe输出文件的文件名,可以通过修改
AssemblyInfo.cs
和项目的csproj
文件来实现 - 参考链接:https://ask.helplib.com/c-Sharp/post_984916
- 注意:上面是手动修改,不是很方便,因此在实际项目中不推荐这样做
csc命令配置程序集信息
windows特殊目录使用
- 参考链接:http://blog.csdn.net/zym2002cn/article/details/6181491
- 在编写应用程序和服务的时候,用户可能不一样,因此特殊目录的所在位置也是不同的,应用程序中,用户主要是普通用户,而服务一般是以System这个特殊用户执行的,因此在使用特殊目录的时候需要注意