常量,字段,构造方法 调试 ms 源代码 一个C#二维码图片识别的Demo 近期ASP.NET问题汇总及对应的解决办法 c# chart控件柱状图,改变柱子宽度 使用C#创建Windows服务 C#服务端判断客户端socket是否已断开的方法 线程 线程池 Task .NET 单元测试的利剑——模拟框架Moq

时间:2022-10-20 21:20:56

常量,字段,构造方法

常量

1.什么是常量

​ 常量是值从不变化的符号,在编译之前值就必须确定。编译后,常量值会保存到程序集元数据中。所以,常量必须是编译器识别的基元类型的常量,如:Boolean,Char,Byte,SByte,...,...,...,UInt64,Single,Double,Decimal,String。另外,C#是可以定义非基元类型的常量的,前提是值必须为null。

public sealed class SomeType
{
    public const SomeType Empty=null;
}

2.常量的特性

  • 常量成员将创建元数据,它是直接嵌入在代码内部,运行时不需要额外分配内存。

  • 常量被视为静态成员,而不是实例成员。

  • 不能获取常量的地址

  • 不能以引用的方式传递常量

  • 参考上面的特性,如果跨程序引用,尝试改变常量初始值,不仅dll需要重新编译,引用者也需要编译

字段

1.什么是字段

字段是一种数据成员,它可以是值类型的实例也可以是引用类型的引用。

CLR支持类型字段和实例字段,什么是类型字段?它其实就是我们熟悉的静态字段,实例字段就是非静态字段。

1.1类型字段(静态字段)的内存分配过程

类型对象(静态对象)是在类型加载到一个AppDomain时创建的,而所需内存也是在内型对象中分配的。

接着上面的问题,那么,什么时候将类型加载到AppDomain中内?当第一次对引用到该类型的方法进行JIT编译时,

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的值。

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

实例构造器的特性:

  • 实例构造器永远不能被继承,类必须执行自己的构造函数。如果没有,系统默认会构造一个无参的。

  • 所以,实例构造器不能用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有时不会调用值类型的静态类型构造器。


常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq
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");
        }
    }
常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

上述代码,BaseClass中和StructValType中都有static构造函数,再对两个类进行实例时,你可以发现值类型的静态函数是没有被调用的。

注意:单个线程中,两个类型构造器包含互相引用的代码可能出问题,因为你无法把握两者的实现顺序,也就无法保证能正确的引用。因为是CLR负责类型构造器的调用,所以不能要求以特定的顺序调用类型构造器。

如果,类型构造器抛出未处理的异常,CLR会认为类型不可用。试图访问该类型的任何字段和方法都会抛出System.TypeInitializationException异常。

调试 ms 源代码

如果需要调试 WPF 源代码或框架源代码,那么需要使用 DotPeek

首先需要下载 dotPeek ,可以到官网下载 dotPeek: Free .NET Decompiler & Assembly Browser by JetBrains 还可以到 csdn 下载

首先打开 dotPeek 然后点击启动符号服务器,所有符号。

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

然后点击工具设置,可以看到这个页面

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

然后打开 VS 工具选项,在调试设置符号,刚才已经复制了,现在添加就好

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

然后还需要去掉微软的服务和本地缓存

然后写一个呆磨进行测试

现在就可以开始调试框架源代码了

只需要在一些函数使用断点,然后堆栈跳转,假如我在 MouseDown 写一个断点,在触发按下,点击堆栈,可以看到外部代码。右击外部代码显示,这样就可以看到 垃圾wr 做的,双击他,可以跳到一个页面,点击加载就可以。

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

这时候可以看到 dotPeek 在反编译,这个时间比较长,需要去做一些你喜欢做的事情,回来就可以发现 dotPeek 反编译好而且你看到 ms 源代码,这时候可以尝试源代码断点,但是不是所有地方都可以断点。

如果你发现无法进入代码,那么尝试安装 Resharper ,如果还是不行,那么需要问一下,是不是使用 UWP ,因为现在我尝试 UWP 还没有成功。

如果还是无法成功,不要来问我,我教了几个小伙伴,有几个是没法进入代码,使用方法都一样,我自己去他电脑弄了,结果我无法进入。

那么接下来就是调试 ms 源代码了,因为已经进入了 Release 的反编译代码,所以通过堆栈调用就进入了源代码,在需要的地方使用断点,当然,不是所有地方可以使用断点。但是进入之后还是可以和原来的调试自己代码一样,看到没有被优化掉的参数的值,可以修改这些值,可以进入其他地方代码设置断点,设置条件,已经使用单步调试跟着代码。

在 win10 下,调试的代码是没有注释的,但是可以对比 dotpeek 的代码来看,一般他里面的代码就是有注释的,反编译的代码和 dotPeek 看到代码有些地方是不同的,但是实际功能是一样的。但是微软源代码使用的框架可能和自己的不一样,看起来代码还是不相同。

最好是自己去下载微软源代码,然后把他放在一个仓库,这样可以看到不同的框架修改的代码。

因为 UWP 编译使用 .netNative ,很多底层都是使用 C++ 写的,所以无法对 UWP 进行反编译

我搭建了自己的博客 https://lindexi.gitee.io/ 欢迎大家访问,里面有很多新的博客。只有在我看到博客写成熟之后才会放在csdn或博客园,但是一旦发布了就不再更新。

如果在博客看到有任何不懂的,欢迎交流。

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接:http://blog.csdn.net/lindexi_gd ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系

博客园博客只做备份,博客发布就不再更新,如果想看最新博客,请到 https://lindexi.oschina.io/

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq
本作品采用知识共享署名-非商业性使用-相同方式共享 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即可(运行环境上文已附链接)。

后续更新一个批量生成二维码图片的工具,网上除了在线生成的,下载下来的工具都不怎么好用。

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq
 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 }
常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

代码链接:(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控件柱状图,改变柱子宽度

让柱状图紧挨

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

改变柱状图宽度

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

chart1.Series[0]["PointWidth"] = "2";

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

使用C#创建Windows服务

使用C#创建Windows服务

本文属于原创,转载请注明出处,谢谢!

一、开发环境

操作系统:Windows 10 X64

开发环境:VS2015

编程语言:C#

.NET版本:.NET Framework 4.0

目标平台:X86

二、创建Windows Service

1、新建一个Windows Service,并将项目名称改为“MyWindowsService”,如下图所示:

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

2、在解决方案资源管理器内将Service1.cs改为MyService1.cs后并点击“查看代码”图标按钮进入代码编辑器界面,如下图所示:

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

3、在代码编辑器内如入以下代码,如下所示:

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq
常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq
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},服务停止!");
            }
        }
    }
}
常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq
常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

4、双击项目“MyWindowsService”进入“MyService”设计界面,在空白位置右击鼠标弹出上下文菜单,选中“添加安装程序”,如下图所示:

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

5、此时软件会生成两个组件,分别为“serviceInstaller1”及“serviceProcessInstaller1”,如下图所示:

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

6、点击“serviceInstaller1”,在“属性”窗体将ServiceName改为MyService,Description改为我的服务,StartType保持为Manual,如下图所示:

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

7、点击“serviceProcessInstaller1”,在“属性”窗体将Account改为LocalSystem(服务属性系统级别),如下图所示:

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

8、鼠标右键点击项目“MyWindowsService”,在弹出的上下文菜单中选择“生成”按钮,如下图所示:

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

9、至此,Windows服务已经创建完毕。

三、创建安装、启动、停止、卸载服务的Windows窗体

1、在同一个解决方案里新建一个Windows Form项目,并命名为WindowsServiceClient,如下图所示:

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

2、将该项目设置为启动项目,并在窗体内添加四个按钮,分别为安装服务、启动服务、停止服务及卸载服务,如下图所示:

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

3、按下F7进入代码编辑界面,引用“System.ServiceProcess”及“System.Configuration.Install”,并输入如下代码:

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq
常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq
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();
                }
            }
        }
    }
}
常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq
常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

4、为了后续调试服务及安装卸载服务的需要,将已生成的MyWindowsService.exe引用到本Windows窗体,如下图所示:

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

5、由于需要安装服务,故需要使用UAC中Administrator的权限,鼠标右击项目“WindowsServiceClient”,在弹出的上下文菜单中选择“添加”->“新建项”,在弹出的选择窗体中选择“应用程序清单文件”并单击确定,如下图所示:

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

6、打开该文件,并将<requestedExecutionLevel level="asInvoker" uiAccess="false" />改为<requestedExecutionLevel  level="requireAdministrator" uiAccess="false" />,如下图所示:

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

7、IDE启动后,将会弹出如下所示的窗体(有的系统因UAC配置有可能不显示),需要用管理员权限打开:

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

8、重新打开后,在IDE运行WindowsServiceClient项目;

9、使用WIN+R的方式打开运行窗体,并在窗体内输入services.msc后打开服务,如下图所示:

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

10、点击窗体内的“安装服务”按钮,将会在服务中出现MyService,如下图所示:

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

11、点击“运行服务”按钮,将启动并运行服务,如下所示:

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

12、点击“停止服务”按钮,将会停止运行服务,如下图所示:

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

13、点击“卸载服务”按钮,将会从服务中删除MyService服务。

14、以上启动及停止服务将会写入D:\MyServiceLog.txt,内容如下所示:

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

源代码下载:

补充:如何调试服务

1、要调试服务,其实很简单,如需将服务附加进程到需要调试的项目里面即可,假如要调试刚才建的服务,现在OnStop事件里设置断点,如下所示:

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

2、启动“WindowsServiceClient”项目,在“调试”菜单中选择“附件到进程”(服务必须事先安装),如下所示:

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

3、找到“MyWindowsService.exe”,点击“附加”按钮,如下图所示:

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

4、点击“停止服务”按钮,程序将会在设置断点的地方中断,如下图所示:

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

出处: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的状态。看下面的代码:

服务端代码:

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq
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();

        }
    }
常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

转自:http://hi.baidu.com/jack1865/item/3dcba2d3b0e2e29932db904d

线程 线程池 Task

首先声明 这是读了 愉悦的绅士 文章

菜鸟之旅——学习线程(线程和线程池)

Task与线程

的一些个人总结,还是那句话,如有不对,欢迎指正

文章以代码加注释的方法展示。

//线程的创建,阻塞和同步

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq
   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();
        }
常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

//对方法加锁

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq
   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("--------------------------------");
            //}

        }
常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

//线程池

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq
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();
        }
常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

//Task 任务  推荐使用任务来做多线程的,便于管理

常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq
  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();
            }
        }
常量,字段,构造方法  调试 ms 源代码  一个C#二维码图片识别的Demo  近期ASP.NET问题汇总及对应的解决办法  c# chart控件柱状图,改变柱子宽度  使用C#创建Windows服务  C#服务端判断客户端socket是否已断开的方法  线程 线程池 Task  .NET 单元测试的利剑——模拟框架Moq

.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 看到更多关于“为什么?”的细节以及一些不错的截图信息。

下载安装

在 nuget 下载安装,并可以在上面查看 api文档

kzu's blog 上看到更多关于mock的通知。从Scott Hanselman 获得一些模拟状态的背景知识

特征

Moq提供下面这些特征

  • 强类型:没有字符串的期望,没有object类型的返回值或者是约束
  • 智能提示:所有的一切都完美支持VS智能感知,从设置期望值,到指定方法调用参数,返回值等等。
  • 不需要了解Record/Replay的习惯。只需要构造你的模拟,设置好并使用它,可选的验证它(你也许不做验证,当它作为存根(stubs)时,或者当你在做更传统的基于状态的测试时,通过检查对象返回的值)
  • 前三点的影响,学习曲线非常低。在大多时候,你无需阅读文档。
  • 通过简单的MockBehaviorm枚举对模拟的行为细粒度控制(Granular control)(无需模拟,存根(stub),伪造(fake),动态模拟等等)
  • 接口和类都能模拟
  • 覆盖期望:能设置默认的期望在一个fixture设置,并且根据测试需要覆盖
  • 为模拟类传递构造函数参数
  • 在模拟中截断器和触发事件
  • 能支持out/ref参数
希望有个生活精彩的程序人生