1.什么是常量
常量是值从不变化的符号,在编译以前值就必须肯定。编译后,常量值会保存到程序集元数据中。因此,常量必须是编译器识别的基元类型的常量,如:Boolean,Char,Byte,SByte,...,...,...,UInt64,Single,Double,Decimal,String。另外,C#是能够定义非基元类型的常量的,前提是值必须为null。html
public sealed class SomeType { public const SomeType Empty=null; }
2.常量的特性
-
常量成员将建立元数据,它是直接嵌入在代码内部,运行时不须要额外分配内存。jquery
-
常量被视为静态成员,而不是实例成员。git
-
不能获取常量的地址github
-
不能以引用的方式传递常量web
-
参考上面的特性,若是跨程序引用,尝试改变常量初始值,不只dll须要从新编译,引用者也须要编译数据库
字段
1.什么是字段
字段是一种数据成员,它能够是值类型的实例也能够是引用类型的引用。编程
CLR支持类型字段和实例字段,什么是类型字段?它其实就是咱们熟悉的静态字段,实例字段就是非静态字段。json
1.1类型字段(静态字段)的内存分配过程
类型对象(静态对象)是在类型加载到一个AppDomain时建立的,而所需内存也是在内型对象中分配的。c#
接着上面的问题,那么,何时将类型加载到AppDomain中内?当第一次对引用到该类型的方法进行JIT编译时,api
1.2实例字段的内存分配过程
实例字段的内存,是在构造容纳字段的类型进行实例构造时分配的。
2.字段特性
字段存储在动态内存中,它不像常量,因此只能在程序运行时,才可以获取到它的值。字段能够是任何类型,不像常量有类型上的限制。
2.1字段修饰符
Static | static | 指定字段为类型的一部分,而不是对象的一部分 |
---|---|---|
Instance | 默认 | 指定字段与实例关联,而不是和类自己关联 |
InitOly | readonly | 只能在构造器方法中进行值的写入,不然只读 |
Volatile | volatile | 表示,编译器和CLR以及硬件,不会对这种字段标识的代码执行“线程不安全的措施”,只有CLR中的基元类型能使用这个修饰符。 |
2.2 readonly和read/write
一般,字段都是read/write,便可读可写的,这也意味着,字段的值会随着运行可能发生值得变化。而当你把字段标记为readonly,那么你就只能在构造函数中,对它进行赋值,编译器是不会容许你在构造器(构造函数)觉得的任何方法写入值,或变动值。
固然,C#提供了一种内联初始化的语法糖来进行readonly值的初始化,这种语法也能够对常量和其余形式的字段进行赋值。
public readonly int =250;
固然,使用内联语法,而不是在构造器中构造,滥用的话可能会有一些性能问题(代码膨胀等)。
构造方法
实例构造器(引用类型)
什么是构造器?
构造器是将类型的实例初始化到良好状态的特殊方法。在“方法定义元数据表”中始终叫.ctor(constructor的简称)。
引用类型在内存中如何实例化?
首先为实例的数据字段分配内存空间,而后是为初始化对象的附加字段(没错,就是咱们常常会提到的同步块索引和类型对象指针)分配内存,而后最后开辟一个空间来调用实例构造函数进行对象的初始化。
在调用构造器以前,为对象分配的内存老是先被归零,为了保证那些被构造器显示重写的字段都得到0或者null的值。
实例构造器的特性:
-
实例构造器永远不能被继承,类必须执行本身的构造函数。若是没有,系统默认会构造一个无参的。
-
因此,实例构造器不能用new ,override,sealed和abstract修饰
-
若是类的修饰符为abstract,那么构造器可访问性默认为protected,不然默认为public。
-
若是基类没有提供无参构造函数(意味着显示的实现了有参的构造函数),那么派生类必须显示调用一个基类的构造器(及为了保证参数一致),不然编译报错。
-
static(sealed和abstract)修饰的类,编译器不会为它生成默认的构造函数
-
一般状况下,不管如何实例化派生类,基类的构造函数必定会被调用,因此object的构造函数必定会被先调用,可是实时上它什么也不会干。
-
极少数状况下,对象实例不会调用构造函数。如,Object的MemberwiseClone方法,它是用来分配内存,初始化对象的附加字段的,而后将源对象的字节数据复制到新对象中。
-
Notice:不要在构造器中调用虚方法。由于,假如被实例化的类型重写了虚方法,就会执行派生类型中的实现,但这个时候,倒是没有初始化的,因此,容易致使没法预测的行为。
内联语法(在字段一节提到过)方式实现初始化实例字段,其实也是转换成构造器方法中的代码来实现。
实例构造器(值类型)
CLR是容许值类型建立实例,可是c#编译器是不会默认为值类型构建构造函数的,而且值类型构造器必须显示调用才执行。如上面所说,即便你本身定义了一个构造函数,无论它是有参仍是无参,编译器都不会去自动调用它,若是你想执行,必须本身显示进行调用。
然而,上面说那么多,在C#中,编译器根本不容许你定义值类型的无参构造函数,它会报:error CS0568:结构不能包含显示的无参构造函数。
同理,你不能对值类型的字段成员进行内联赋值,由于内联语句其实是经过构造器进行赋值,以下面的代码:
internal struct SomeValType { private int m=5; }
上面的代码,会报:结构中不能有实例字段初始值设定项。
因此,值类型的字段老是被初始化为0或null,由于没有真正意义上的构造函数为它初始化其余值,只有你手动去调用构造函数(因此这里咱们不理解为初始化)。
当你提供一个有参构造函数时,你须要为全部的字段进行赋值,不然会报:error CS0171:在控制返回到调用方法以前,字段XXX必须彻底赋值。
类型构造器(静态构造器)
什么是类型构造器?
实例构造器是为了让类的实例有一个良好的可验证的初始值。而类型构造器是为静态类型服务,顾名思义,类型构造器则是为了让类型有良好的初始状态。
类型构造器特征
-
默认没有构造函数
-
(类型)静态构造器永远不能有参数
-
必须标记为static,由于静态类型的成员必须为静态成员
-
不能赋予任何访问修饰符,默认为隐式类型,C#默认为private
-
类型构造器中的代码只能访问类型的静态字段(常规用途就是初始化这些字段)
类型构造器的调用过程
类型构造器调用过程大体以下:
JIT编译器在编译到一个静态方法时,会查看引用了哪些静态类型。若是这个静态类型定义了一个构造函数,JIT编译器会检查当前AppDomain,是否已经执行过了这个类型构造器。若是已经执行过,就不添加对它的调用。若是从未执行过,JIT编译器会在它的本机代码中添加对类型构造器的调用。
重要的是:为何静态类型的特性是十分适合作单例呢?由于CLR经常是确保每个AppDomain中,一个类型构造器都只执行一次,那么上述的机制不足以很好的支撑这个特性,由于,多个线程下如何保证呢?为了保证这一点,调用类型构造器时,每个调用线程都会获取一个互斥线程同步锁,在这样的机制下,若是多个线程试图同时调用某个类型的静态构造器,只有一个线程能够得到锁,其余的线程会被阻塞。只有第一个线程会执行静态构造器的代码。当一个线程离开构造器后,正在等待的线程才会被唤醒,后面的线程会发现,类型构造器已经被执行过了,将直接从构造方法返回。这样就能确保不会被再次调用。而且以上是线程安全的。
因此,单例模式就是借助上面的特性,你想构建的单例对象,则也应该放到类型构造器中进行初始化。
注意:值类型中也能够定义类型(静态)构造器,可是是不推荐这么作的,由于有时候CLR有时不会调用值类型的静态类型构造器。
internal struct StructValType { //虽然值类型的构造函数必须有参数,可是这个是静态构造函数,因此它是必定没有参数的,也不用遵照,必须初始化全部成员的值 static StructValType() { Console.WriteLine("我会出现吗?"); } public int x; } class BaseClass { public string ClassName { get; set; } static BaseClass() { Console.WriteLine("I'm BaseClass static Constructor without param"); } public BaseClass() { Console.WriteLine("I'm BaseClass Constructor without param"); } }
上述代码,BaseClass中和StructValType中都有static构造函数,再对两个类进行实例时,你能够发现值类型的静态函数是没有被调用的。
注意:单个线程中,两个类型构造器包含互相引用的代码可能出问题,由于你没法把握二者的实现顺序,也就没法保证能正确的引用。由于是CLR负责类型构造器的调用,因此不能要求以特定的顺序调用类型构造器。
若是,类型构造器抛出未处理的异常,CLR会认为类型不可用。试图访问该类型的任何字段和方法都会抛出System.TypeInitializationException异常。
调试 ms 源代码
若是须要调试 WPF 源代码或框架源代码,那么须要使用 DotPeek
首先须要下载 dotPeek ,能够到官网下载 dotPeek: Free .NET Decompiler & Assembly Browser by JetBrains 还能够到 csdn 下载
首先打开 dotPeek 而后点击启动符号服务器,全部符号。
而后点击工具设置,能够看到这个页面
而后打开 VS 工具选项,在调试设置符号,刚才已经复制了,如今添加就好
而后还须要去掉微软的服务和本地缓存
而后写一个呆磨进行测试
如今就能够开始调试框架源代码了
只须要在一些函数使用断点,而后堆栈跳转,假如我在 MouseDown 写一个断点,在触发按下,点击堆栈,能够看到外部代码。右击外部代码显示,这样就能够看到 垃圾wr 作的,双击他,能够跳到一个页面,点击加载就能够。
这时候能够看到 dotPeek 在反编译,这个时间比较长,须要去作一些你喜欢作的事情,回来就能够发现 dotPeek 反编译好并且你看到 ms 源代码,这时候能够尝试源代码断点,可是不是全部地方均可以断点。
若是你发现没法进入代码,那么尝试安装 Resharper ,若是仍是不行,那么须要问一下,是否是使用 UWP ,由于如今我尝试 UWP 尚未成功。
若是仍是没法成功,不要来问我,我教了几个小伙伴,有几个是无法进入代码,使用方法都同样,我本身去他电脑弄了,结果我没法进入。
那么接下来就是调试 ms 源代码了,由于已经进入了 Release 的反编译代码,因此经过堆栈调用就进入了源代码,在须要的地方使用断点,固然,不是全部地方可使用断点。可是进入以后仍是能够和原来的调试本身代码同样,看到没有被优化掉的参数的值,能够修改这些值,能够进入其余地方代码设置断点,设置条件,已经使用单步调试跟着代码。
在 win10 下,调试的代码是没有注释的,可是能够对比 dotpeek 的代码来看,通常他里面的代码就是有注释的,反编译的代码和 dotPeek 看到代码有些地方是不一样的,可是实际功能是同样的。可是微软源代码使用的框架可能和本身的不同,看起来代码仍是不相同。
最好是本身去下载微软源代码,而后把他放在一个仓库,这样能够看到不一样的框架修改的代码。
由于 UWP 编译使用 .netNative ,不少底层都是使用 C++ 写的,因此没法对 UWP 进行反编译
我搭建了本身的博客 https://lindexi.gitee.io/ 欢迎你们访问,里面有不少新的博客。只有在我看到博客写成熟以后才会放在csdn或博客园,可是一旦发布了就再也不更新。
若是在博客看到有任何不懂的,欢迎交流。
本做品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、从新发布,但务必保留文章署名林德熙(包含连接:http://blog.csdn.net/lindexi_gd ),不得用于商业目的,基于本文修改后的做品务必以相同的许可发布。若有任何疑问,请与我联系。
博客园博客只作备份,博客发布就再也不更新,若是想看最新博客,请到 https://lindexi.oschina.io/

本做品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、从新发布,但务必保留文章署名[林德熙](http://blog.csdn.net/lindexi_gd)(包含连接:http://blog.csdn.net/lindexi_gd ),不得用于商业目的,基于本文修改后的做品务必以相同的许可发布。若有任何疑问,请与我[联系](mailto:lindexi_gd@163.com)。
一个C#二维码图片识别的Demo
怎么用NuGet和怎么配置log4net就不介绍了,直接上代码(Visual Studio 2015 下的项目,用的.NET Framework 4.5.2)。
其中QRDecodeConsoleApp.exe.config文件里配置图片路劲(默认为D:\个人文档\Pictures\二维码)、图片类型(默认为*.png)。
也支持在命令行里执行,exe后接图片路劲参数。
须要直接用的朋友,确认完QRDecodeDemo\bin\Debug下的配置文件QRDecodeConsoleApp.exe.config后,运行QRDecodeConsoleApp.exe便可(运行环境上文已附连接)。
后续更新一个批量生成二维码图片的工具,网上除了在线生成的,下载下来的工具都不怎么好用。
1 using System; 2 using System.IO; 3 using System.Drawing; 4 using System.Configuration; 5 using ThoughtWorks.QRCode.Codec; 6 using ThoughtWorks.QRCode.Codec.Data; 7 using log4net; 8 9 namespace QRDecodeConsoleApp 10 { 11 class Program 12 { 13 /// <summary> 14 /// 私有日志对象 15 /// </summary> 16 private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); 17 18 /// <summary> 19 /// 识别指定目录下的所有二维码图片(默认是PNG) 20 /// </summary> 21 /// <param name="args"></param> 22 static void Main(string[] args) 23 { 24 try 25 { 26 string[] files; 27 if (args.Length > 0) 28 { 29 //args[0]为CMD里exe后的第一个参数 ImgType默认配置的*.png 30 files = Directory.GetFiles(args[0], ConfigurationManager.AppSettings["ImgType"]); 31 } 32 else 33 { 34 //读取指定路劲(QRDecodeConsoleApp.exe.config里配置的路劲) 35 files = Directory.GetFiles(ConfigurationManager.AppSettings["QRImgPath"], 36 ConfigurationManager.AppSettings["ImgType"]); 37 } 38 39 //存放结果的文件 40 string filePath = "txtResult" + DateTime.Now.ToString("yyyyMMddHHmmssfff") + ".config"; 41 42 //一个个读取并追加到记录文件 43 for (int i = 0; i < files.Length; i++) 44 { 45 File.AppendAllText(filePath, CodeDecoder(files[i]) + "\t" + files[i] + "\n");//追加到文件里记录 46 logger.Info("第" + i + "个识别成功"); 47 Console.WriteLine("第" + i + "个识别成功"); 48 } 49 Console.WriteLine("识别完成,按任意键退出"); 50 Console.ReadLine(); 51 } 52 catch (Exception ex) 53 { 54 Console.WriteLine("识别出错:" + ex.Message); 55 logger.Error("识别出错"); 56 logger.Error("异常描述:\t" + ex.Message); 57 logger.Error("异常方法:\t" + ex.TargetSite); 58 logger.Error("异常堆栈:\t" + ex.StackTrace); 59 Console.ReadLine(); 60 } 61 62 } 63 64 /// <summary> 65 /// 读取图片文件,识别二维码 66 /// </summary> 67 /// <param name="filePath">图片文件路劲</param> 68 /// <returns>识别结果字符串</returns> 69 public static string CodeDecoder(string filePath) 70 { 71 string decoderStr; 72 try 73 { 74 if (!System.IO.File.Exists(filePath))//判断有没有须要读取的主文件夹,若是不存在,终止 75 return null; 76 77 Bitmap bitMap = new Bitmap(Image.FromFile(filePath));//实例化位图对象,把文件实例化为带有颜色信息的位图对象 78 QRCodeDecoder decoder = new QRCodeDecoder();//实例化QRCodeDecoder 79 80 //经过.decoder方法把颜色信息转换成字符串信息 81 decoderStr = decoder.decode(new QRCodeBitmapImage(bitMap), System.Text.Encoding.UTF8); 82 } 83 catch (Exception ex) 84 { 85 throw ex; 86 } 87 88 return decoderStr;//返回字符串信息 89 } 90 91 92 } 93 }
代码连接:(QRDecodeDemo.zip)
近期ASP.NET问题汇总及对应的解决办法
1. 使用SQL统计一个字符串中指定字符的个数,示例(统计0的个数):
select len('402301001') - len(replace('402301001','0',''))
2. 使用Forms认证,客户端本地时间不对没法登录系统,解决办法:
FormsAuthentication.RedirectFromLoginPage第二个参数设置成false,MSDN资料:http://msdn.microsoft.com/zh-cn/library/ka5ffkce(v=vs.110).aspx
3. 网站服务器CPU100%,找到具体是IIS中哪一个网站致使的:
①首先设置任务管理器的查看方式,加入PID的显示;
②用C:\Windows\System32\inetsrv>appcmd list wp命令定位到具体网站PID(此处为Server 2008里的命令,Server 2003是 iisapp -a 命令);
4. jQuery1.6中attr("checked")无效,正确写法:
三种写法:if ( elem.checked )或if ( $( elem ).prop( "checked" ) )或if ( $( elem ).is( ":checked" ) )
官方文档:http://api.jquery.com/attr/
5. n多log4net的日志文件,只能借助editplus查找。解决办法:用Log Parser Lizard 或者SQL SERVER 中写查询
6. 网站提示访问IIS元数据库失败,解决办法(命令):aspnet_regiis -ga ASPNET
7. 远程桌面关闭了Explorer进程,怎么打开远程桌面的任务管理器: Ctrl+Shift+Esc
8. .NET2.0版序列化DataSet序列化为json,json反序列化为DataSet:http://json.codeplex.com/
9. 将EXCEL数据快速生成组织结构图:http://www.visio123.com/Visio_2010/Visiokuachengxushiyong/20130225/33.html
10. web.config上传文件大小设置(Windows Server 2008与之前的Windows Server 2003设置不同):
Windows Server 2003中web.config配置为在system.web节点下添加以下配置:
<!--100MB--> <httpRuntime maxRequestLength="102400" useFullyQualifiedRedirectUrl="true" />
Windows Server 2008中web.config还得在system.webServer节点下添加以下配置:
<security> <requestFiltering> <requestLimits maxAllowedContentLength="102400000" /> </requestFiltering> </security>
具体设置方法,参考:http://www.cnblogs.com/henryhappier/archive/2010/09/20/1832098.html。实际测试中发现配置成102400000传20MB的文件就出异常了,配置成1024000000就没问题……
c# chart控件柱状图,改变柱子宽度
让柱状图紧挨
改变柱状图宽度
chart1.Series[0]["PointWidth"] = "2";
使用C#建立Windows服务
使用C#建立Windows服务
本文属于原创,转载请注明出处,谢谢!
1、开发环境
操做系统:Windows 10 X64
开发环境:VS2015
编程语言:C#
.NET版本:.NET Framework 4.0
目标平台:X86
2、建立Windows Service
一、新建一个Windows Service,并将项目名称改成“MyWindowsService”,以下图所示:
二、在解决方案资源管理器内将Service1.cs改成MyService1.cs后并点击“查看代码”图标按钮进入代码编辑器界面,以下图所示:
三、在代码编辑器内如入如下代码,以下所示:
using System; using System.ServiceProcess; using System.IO; namespace MyWindowsService { public partial class MyService : ServiceBase { public MyService() { InitializeComponent(); } string filePath = @"D:\MyServiceLog.txt"; protected override void OnStart(string[] args) { using (FileStream stream = new FileStream(filePath,FileMode.Append)) using (StreamWriter writer = new StreamWriter(stream)) { writer.WriteLine($"{DateTime.Now},服务启动!"); } } protected override void OnStop() { using (FileStream stream = new FileStream(filePath, FileMode.Append)) using (StreamWriter writer = new StreamWriter(stream)) { writer.WriteLine($"{DateTime.Now},服务中止!"); } } } }
四、双击项目“MyWindowsService”进入“MyService”设计界面,在空白位置右击鼠标弹出上下文菜单,选中“添加安装程序”,以下图所示:
五、此时软件会生成两个组件,分别为“serviceInstaller1”及“serviceProcessInstaller1”,以下图所示:
六、点击“serviceInstaller1”,在“属性”窗体将ServiceName改成MyService,Description改成个人服务,StartType保持为Manual,以下图所示:
七、点击“serviceProcessInstaller1”,在“属性”窗体将Account改成LocalSystem(服务属性系统级别),以下图所示:
八、鼠标右键点击项目“MyWindowsService”,在弹出的上下文菜单中选择“生成”按钮,以下图所示:
九、至此,Windows服务已经建立完毕。
3、建立安装、启动、中止、卸载服务的Windows窗体
一、在同一个解决方案里新建一个Windows Form项目,并命名为WindowsServiceClient,以下图所示:
二、将该项目设置为启动项目,并在窗体内添加四个按钮,分别为安装服务、启动服务、中止服务及卸载服务,以下图所示:
三、按下F7进入代码编辑界面,引用“System.ServiceProcess”及“System.Configuration.Install”,并输入以下代码:
using System; using System.Collections; using System.Windows.Forms; using System.ServiceProcess; using System.Configuration.Install; namespace WindowsServiceClient { public partial class Form1 : Form { public Form1() { InitializeComponent(); } string serviceFilePath = $"{Application.StartupPath}\\MyWindowsService.exe"; string serviceName = "MyService"; //事件:安装服务 private void button1_Click(object sender, EventArgs e) { if (this.IsServiceExisted(serviceName)) this.UninstallService(serviceName); this.InstallService(serviceFilePath); } //事件:启动服务 private void button2_Click(object sender, EventArgs e) { if (this.IsServiceExisted(serviceName)) this.ServiceStart(serviceName); } //事件:中止服务 private void button4_Click(object sender, EventArgs e) { if (this.IsServiceExisted(serviceName)) this.ServiceStop(serviceName); } //事件:卸载服务 private void button3_Click(object sender, EventArgs e) { if (this.IsServiceExisted(serviceName)) { this.ServiceStop(serviceName); this.UninstallService(serviceFilePath); } } //判断服务是否存在 private bool IsServiceExisted(string serviceName) { ServiceController[] services = ServiceController.GetServices(); foreach (ServiceController sc in services) { if (sc.ServiceName.ToLower() == serviceName.ToLower()) { return true; } } return false; } //安装服务 private void InstallService(string serviceFilePath) { using (AssemblyInstaller installer = new AssemblyInstaller()) { installer.UseNewContext = true; installer.Path = serviceFilePath; IDictionary savedState = new Hashtable(); installer.Install(savedState); installer.Commit(savedState); } } //卸载服务 private void UninstallService(string serviceFilePath) { using (AssemblyInstaller installer = new AssemblyInstaller()) { installer.UseNewContext = true; installer.Path = serviceFilePath; installer.Uninstall(null); } } //启动服务 private void ServiceStart(string serviceName) { using (ServiceController control = new ServiceController(serviceName)) { if (control.Status == ServiceControllerStatus.Stopped) { control.Start(); } } } //中止服务 private void ServiceStop(string serviceName) { using (ServiceController control = new ServiceController(serviceName)) { if (control.Status == ServiceControllerStatus.Running) { control.Stop(); } } } } }
四、为了后续调试服务及安装卸载服务的须要,将已生成的MyWindowsService.exe引用到本Windows窗体,以下图所示:
五、因为须要安装服务,故须要使用UAC中Administrator的权限,鼠标右击项目“WindowsServiceClient”,在弹出的上下文菜单中选择“添加”->“新建项”,在弹出的选择窗体中选择“应用程序清单文件”并单击肯定,以下图所示:
六、打开该文件,并将<requestedExecutionLevel level="asInvoker" uiAccess="false" />改成<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />,以下图所示:
七、IDE启动后,将会弹出以下所示的窗体(有的系统因UAC配置有可能不显示),须要用管理员权限打开:
八、从新打开后,在IDE运行WindowsServiceClient项目;
九、使用WIN+R的方式打开运行窗体,并在窗体内输入services.msc后打开服务,以下图所示:
十、点击窗体内的“安装服务”按钮,将会在服务中出现MyService,以下图所示:
十一、点击“运行服务”按钮,将启动并运行服务,以下所示:
十二、点击“中止服务”按钮,将会中止运行服务,以下图所示:
1三、点击“卸载服务”按钮,将会从服务中删除MyService服务。
1四、以上启动及中止服务将会写入D:\MyServiceLog.txt,内容以下所示:
源代码下载:
补充:如何调试服务
一、要调试服务,其实很简单,如需将服务附加进程到须要调试的项目里面便可,假如要调试刚才建的服务,如今OnStop事件里设置断点,以下所示:
二、启动“WindowsServiceClient”项目,在“调试”菜单中选择“附件到进程”(服务必须事先安装),以下所示:
三、找到“MyWindowsService.exe”,点击“附加”按钮,以下图所示:
四、点击“中止服务”按钮,程序将会在设置断点的地方中断,以下图所示:
出处:http://www.cnblogs.com/cncc/p/7170951.html
C#服务端判断客户端socket是否已断开的方法
刚开始,用Socket类的Connected属性来实现,却发现行不通,connected只表示 是在上次 仍是 操做时链接到远程主机。若是在这以后[链接的另外一方]断开了,它还一直返回true, 除非你再经过socket来发送数据。因此经过个属性来判断是行不通的!
后来有人说能够用Socket.Available属性来判断,Socket.Available表示获取已经从网络接收且可供读取的数据量。
msdn中说:若是[链接的另外一方]断开了,它就会抛出异常。然而,这个BUG报告(http://dam.mellis.org/2004/08/net_socket_bugs_gotchas/)却指出:msdn的说法并不彻底正确,这个属性只有在少数状况下才抛出异常。因此,这一招仍是行不通!
最后使用socket.Poll()方法来完成实现,此方法是肯定socket的状态。看下面的代码:
服务端代码:
class Program { private static List<Socket> list=new List<Socket>(); static void Main(string[] args) { Timer timer=new Timer(1000); timer.Elapsed += new ElapsedEventHandler(timer_Elapsed); timer.Start(); Thread thread = new Thread(Listener); thread.Start(); } //每秒服务端向客户端推送 static void timer_Elapsed(object sender, ElapsedEventArgs e) { if (list.Count > 0) { for (int i = list.Count-1; i >=0; i--) { string sendStr = "Server Information"; byte[] bs = Encoding.ASCII.GetBytes(sendStr); if (list[i].Poll(1000, SelectMode.SelectRead))
//SelectMode.SelectRead表示,若是已调用 而且有挂起的链接,true。 //- 或 - 若是有数据可供读取,则为 true。- 或 - 若是链接已关闭、重置或终止,则返回 true(此种状况就表示若客户端断开链接了,则此方法就返回true); 不然,返回 false。
{ list[i].Close();//关闭socket list.RemoveAt(i);//从列表中删除断开的socke continue; } list[i].Send(bs, bs.Length, 0); } } } public static void Listener() { int port = 11000; string host = "192.168.7.36"; /**/ ///建立终结点(EndPoint) IPAddress ip = IPAddress.Parse(host);//把ip地址字符串转换为IPAddress类型的实例 IPEndPoint ipe = new IPEndPoint(ip, port);//用指定的端口和ip初始化IPEndPoint类的新实例 /**/ ///建立socket并开始监听 Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//建立一个socket对像,若是用udp协议,则要用SocketType.Dgram类型的套接字 s.Bind(ipe);//绑定EndPoint对像(2000端口和ip地址) s.Listen(10);//开始监听 Console.WriteLine("等待客户端链接"); while (true) { /**/ ///接受到client链接,为此链接创建新的socket,并接受信息 list.Add(s.Accept());//为新建链接建立新的socket Console.WriteLine("创建链接"); string recvStr = ""; byte[] recvBytes = new byte[1024]; int bytes; bytes = list[list.Count-1].Receive(recvBytes, recvBytes.Length, 0);//从客户端接受信息 recvStr += Encoding.ASCII.GetString(recvBytes, 0, bytes); /**/ ///给client端返回信息 Console.WriteLine("server get message:{0}", recvStr);//把客户端传来的信息显示出来 string sendStr = "ok!Client send message successful!"; byte[] bs = Encoding.ASCII.GetBytes(sendStr); list[list.Count-1].Send(bs, bs.Length, 0);//返回信息给客户端 //temp.Close(); } s.Close(); } }
转自:http://hi.baidu.com/jack1865/item/3dcba2d3b0e2e29932db904d
线程 线程池 Task
首先声明 这是读了 愉悦的绅士 文章
《Task与线程》
的一些我的总结,仍是那句话,若有不对,欢迎指正
文章以代码加注释的方法展现。
//线程的建立,阻塞和同步
public static ManualResetEvent MREstop=new ManualResetEvent(false); public static AutoResetEvent AREstop = new AutoResetEvent(false); static void Main(string[] args) { //使用方法注册 Thread Thread1 = new Thread(Method1); //使用Lambda注册 Thread Thread2 = new Thread((s) => { //暂停线程2,使用ManualResetEvent暂停,当使用Set方法的时候会跳过全部WaitOne(); //MREstop.WaitOne(); //暂停主线程,使用AutoResetEvent暂停,当使用Set方法的时候会跳过第一次遇到的WaitOne(); AREstop.WaitOne(); Console.WriteLine("----这是带参数方法2,参数为{0}----",s); Console.WriteLine(DateTime.Now); Console.WriteLine("----方法2结束----"); }); //若直接运行,会发现,Thread1和主线程的代码会交错在一块儿,而Thread2的代码一直在最后出现,这是由于Thread1和主线程一块儿运行,而Thread2延迟运行 Thread1.Start(); Thread2.Start("这是一个参数"); //取消注释,会发现Thread1和Thread2都执行完后,才会执行主线程代码 //Thread1.Join(); //Thread2.Join(); //暂停主线程,使用ManualResetEvent暂停,当使用Set方法的时候会跳过全部WaitOne(); //MREstop.WaitOne(); //暂停主线程,使用AutoResetEvent暂停,当使用Set方法的时候会跳过第一次遇到的WaitOne(); //AREstop.WaitOne(); Console.WriteLine("----这是主线程----"); Console.WriteLine(DateTime.Now); Console.WriteLine("----主线程结束----"); } static void Method1() { Thread.Sleep(1000); Console.WriteLine("----这是不带参数方法1----"); Console.WriteLine(DateTime.Now); Console.WriteLine("----方法1结束----"); //使用线程1开启同步,当使用Set方法的时候会跳过全部WaitOne(); //MREstop.Set(); //使用线程1开启同步,,当使用Set方法的时候会跳过第一次遇到的WaitOne(),因此主要是看Cpu先执行那个进程; //AREstop.Set(); }
//对方法加锁
static readonly object LockObject = new object(); static int i = 100; static void Main(string[] args) { //实例化100条线程,执行同一个方法 for (int i = 0; i < 100; i++) { Thread Thread1 = new Thread(Method1); Thread1.Start(); } } static void Method1() { //若不加锁,全部线程均可以同时访问该方法,会形成显示的结果混乱,而加了锁,就同时只能拥有一个线程访问该方法 //Monitor.Enter(LockObject); //i++非原子性操做,可能同时被多个线程执行,形成竞态,会影响运算结果,因此不能在多线程中使用。 //i++; //推荐使用线程原子性自增操做 System.Threading.Interlocked.Increment(ref i); Thread.Sleep(10); Console.WriteLine("This is Thread{0} and i={1}", Thread.CurrentThread.ManagedThreadId, i); Console.WriteLine("--------------------------------"); //加了锁必须解锁 //Monitor.Exit(LockObject); //或者使用lock(LockObject)的方法,至关于try{Monitor.Enter(LockObject);}catch{}finally{Monitor.Exit(LockObject);}的简便写法 //lock(LockObject) //{ // System.Threading.Interlocked.Increment(ref i); // Thread.Sleep(10); // Console.WriteLine("This is Thread{0} and i={1}", Thread.CurrentThread.ManagedThreadId, i); // Console.WriteLine("--------------------------------"); //} }
//线程池
public static AutoResetEvent AREstop1 = new AutoResetEvent(false); static void Main(string[] args) { AutoResetEvent AREstop2 = new AutoResetEvent(false); //建立而且执行,线程池上限为CPU核心数*250,默认为后台线程 ThreadPool.QueueUserWorkItem(new WaitCallback(Method1), AREstop2); //建立而且执行 ThreadPool.QueueUserWorkItem(new WaitCallback(s => { Thread.Sleep(2000); Console.WriteLine("----这是带参数方法2,参数为{0}----", s); Console.WriteLine(DateTime.Now); Console.WriteLine("----方法2结束----"); AREstop1.Set(); }), "这是一个参数"); //线程池的同步线程和线程一致,可使用ManualResetEvent和AutoResetEvent执行。 //因为线程池没有Join方法,因此可使用WaitAll()方法来达到全部线程执行完毕后执行主线程的效果 List<WaitHandle> handles = new List<WaitHandle>(); handles.Add(AREstop1); // handles.Add(AREstop2); //注意,对多个线程要使用不一样的AutoResetEvent,只要数组中的AutoResetEvent接受到set指令就解锁,若所有为同一个名字 //则只要任何一个进程set以后,就会执行主线程。因为线程池默认为后台线程,一旦执行完成主线程,则其他线程自动结束 //必须数组之中的AutoResetEvent所有set后才会执行,若是该有一个没有set,都不会执行主线程。 //WaitAll最大数组上限为64 WaitHandle.WaitAll(handles.ToArray()); Console.WriteLine("----这是主线程----"); Console.WriteLine(DateTime.Now); Console.WriteLine("----主线程结束----"); } //方法要带一个参数 static void Method1(object obj) { Thread.Sleep(1000); Console.WriteLine("----这是带参数方法1----"); Console.WriteLine(DateTime.Now); Console.WriteLine("----方法1结束----"); AutoResetEvent AREstop2 = (AutoResetEvent)obj ; AREstop2.Set(); }
//Task 任务 推荐使用任务来作多线程的,便于管理
public static AutoResetEvent AREstop1 = new AutoResetEvent(false); static void Main(string[] args) { //Task实例化的都是后台线程,若是要更改成前台线程,须要再方法里面修改 #region Task任务 使用线程池 //{ // //实例化任务,必须手动启动,注意,方法是不能带参数的 // Task TaskFirst = new Task(Method1); // //Status能够标识当前任务的状态 // //Created:表示默认初始化任务,可是“工厂建立的”实例直接跳过。 // //WaitingToRun: 这种状态表示等待任务调度器分配线程给任务执行。 // //RanToCompletion:任务执行完毕。 // Console.WriteLine("TaskFirst的状态:{0}", TaskFirst.Status); // TaskFirst.Start(); // Console.WriteLine("TaskFirst的状态:{0}", TaskFirst.Status); // //工厂建立的直接执行 // Task TaskSecond = Task.Factory.StartNew(() => // { // Console.WriteLine("----这是不带参数方法2----"); // Console.WriteLine(DateTime.Now); // Console.WriteLine("----方法2结束----"); // }); // //使用这种方法删除任务 // //CancellationTokenSource cancelTokenSource = new CancellationTokenSource(); // //Task.Factory.StartNew(() => // //{ // // Console.WriteLine("----这是要删除方法4----"); // // Console.WriteLine(DateTime.Now); // // Console.WriteLine("----要删除方法结束----"); // //}, cancelTokenSource.Token); // //cancelTokenSource.Cancel(); // //流程控制 // { // //没有加标识的默认使用线程池建立,若主线程结束自动结束,因此须要先堵塞主线程 // //AREstop1.WaitOne(); // //或者使用阻塞 // Task.WaitAll(TaskFirst, TaskSecond); // //也可使用Wait()等待单个线程,你会发现下面TaskFirst的状态的状态为Running,由于主线程开始运行了,而线程TaskFirst还在运行中 // //TaskSecond.Wait(); // //Task.WaitAny 只要数组中有一个执行完毕,就继续执行主线程 // //Task.WaitAny(TaskFirst, TaskSecond); // //继续执行,在TaskFirst任务结束后继续执行,此时TaskFirst已经结束。记得加Wait(),不然主线程结束就直接结束了。 // TaskFirst.ContinueWith(NewTask => // { // Console.WriteLine("----这是不带参数方法3----"); // Console.WriteLine(DateTime.Now); // Console.WriteLine("TaskFirst的状态:{0}", TaskFirst.Status); // Console.WriteLine("----方法3结束----"); // }).Wait(); // } // Console.WriteLine("TaskFirst的状态:{0}", TaskFirst.Status); //} #endregion #region Task任务 使用线程 { ////实例化任务,必须手动启动,注意,方法是不能带参数的 //Task TaskFirst = new Task(Method1, TaskCreationOptions.LongRunning); //TaskFirst.Start(); } #endregion #region Task任务 带参数 { Task<int> TaskFirst = new Task<int>(((x) => { return (int)(x); }), 10); TaskFirst.Start(); Console.WriteLine(" result ={0}", TaskFirst.Result); Task<string> TaskSecond = Task<string>.Factory.StartNew(new Func<object, string>(x => { return $"This is {x}"; }), 10); Console.WriteLine(" result ={0}", TaskSecond.Result); } #endregion Console.WriteLine("----这是主线程----"); Console.WriteLine(DateTime.Now); Console.WriteLine("----主线程结束----"); } //C# 6.0只读赋值 static object Locker { get; } = new object(); static void Method1() { lock (Locker) { Thread.CurrentThread.IsBackground = false; Thread.Sleep(1000); Console.WriteLine("----这是带参数方法1----"); Console.WriteLine(DateTime.Now); Console.WriteLine("----方法1结束----"); //AREstop1.Set(); } }
.NET 单元测试的利剑——模拟框架Moq
前言
这篇文章是翻译文,由于经过本身参与的项目,愈加以为单元测试的重要性,特别是当跟业务数据打交道的时候的,Moq就如雪中送炭,因此想学习这个框架,就从这篇译文开始吧,顺便提高下本身英文阅读水平吧,因为英语实在不行,借助有道翻译有时候还理解不了原文的意思。凑合的看吧,下一篇介绍moq的使用demo
原文地址:https://github.com/moq/moq4
什么是Moq
Moq(发音—"mock-you"或是"mock")是一个针对.net开发只用于模拟的库,它充分利用了.NET LINQ表达式树和Lambda表达式的优点,使更具备生产效率,类型安全和友好重构的可模拟的类库。而且能像模拟Class类同样模拟Interface。API很是简洁和直接,不须要太多的前面的知识或是模拟概念的经验。
为何?
由于这个库是由那些没有使用过任何模拟类库的开发者建立的(或是对那些已经实现的库太过复杂而不满),表明性的他们经过手动的去写他们本身的模拟(带着或多或少的“幻想”)。在这种状况下,大多数开发者很是关注实效,遵循TDD。这是一种感受,从其余模拟库中进入的障碍有点高,并且有更简单,更轻量,更优雅的方法是有可能的。Moq经过带着C#简洁优雅以及VB的语言特性他们统一称为LINQ,能知足全部的上面说的这些(并不仅是缩略词LINQ(查询)的意思)。
Moq被设计成为一个很是实用,直接的方式为你的测试去设置依赖。它的API设计甚至帮助初学者用户在“成功的坑”里而且能避免最多见的模拟错误。
当它被构思出来的时候,它就是惟一一个与那些有别于普通的和不直观的(特别是针对新手)记录/回放方法的模拟库框架(这是好的)
不使用Record/Replay也意味着将那些一般的指望转移到一个fixture设置方法是很是简单的,甚至能覆盖那些指望在特定的单元测试中。
你能够在 kzu's blog 看到更多关于“为何?”的细节以及一些不错的截图信息。
下载安装
在kzu's blog 上看到更多关于mock的通知。从Scott Hanselman 得到一些模拟状态的背景知识
特征
Moq提供下面这些特征
- 强类型:没有字符串的指望,没有object类型的返回值或者是约束
- 智能提示:全部的一切都完美支持VS智能感知,从设置指望值,到指定方法调用参数,返回值等等。
- 不须要了解Record/Replay的习惯。只须要构造你的模拟,设置好并使用它,可选的验证它(你也许不作验证,当它做为存根(stubs)时,或者当你在作更传统的基于状态的测试时,经过检查对象返回的值)
- 前三点的影响,学习曲线很是低。在大多时候,你无需阅读文档。
- 经过简单的MockBehaviorm枚举对模拟的行为细粒度控制(Granular control)(无需模拟,存根(stub),伪造(fake),动态模拟等等)
- 接口和类都能模拟
- 覆盖指望:能设置默认的指望在一个fixture设置,而且根据测试须要覆盖
- 为模拟类传递构造函数参数
- 在模拟中截断器和触发事件
- 能支持out/ref参数