常量,字段,構造方法 調試 ms 源代碼 一個C#二維碼圖片識別的Demo 近期ASP.NET問題彙總及對應的解決辦法 c# chart控件柱狀圖,改變柱子寬度 使用C#建立Windows服務 C#服

常量,字段,構造方法

 

常量

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 看到更多關於「爲何?」的細節以及一些不錯的截圖信息。

下載安裝

在 nuget 下載安裝,並能夠在上面查看 api文檔

kzu's blog 上看到更多關於mock的通知。從Scott Hanselman 得到一些模擬狀態的背景知識

特徵

Moq提供下面這些特徵

  • 強類型:沒有字符串的指望,沒有object類型的返回值或者是約束
  • 智能提示:全部的一切都完美支持VS智能感知,從設置指望值,到指定方法調用參數,返回值等等。
  • 不須要了解Record/Replay的習慣。只須要構造你的模擬,設置好並使用它,可選的驗證它(你也許不作驗證,當它做爲存根(stubs)時,或者當你在作更傳統的基於狀態的測試時,經過檢查對象返回的值)
  • 前三點的影響,學習曲線很是低。在大多時候,你無需閱讀文檔。
  • 經過簡單的MockBehaviorm枚舉對模擬的行爲細粒度控制(Granular control)(無需模擬,存根(stub),僞造(fake),動態模擬等等)
  • 接口和類都能模擬
  • 覆蓋指望:能設置默認的指望在一個fixture設置,而且根據測試須要覆蓋
  • 爲模擬類傳遞構造函數參數
  • 在模擬中截斷器和觸發事件
  • 能支持out/ref參數
但願有個生活精彩的程序人生
相關文章
相關標籤/搜索