預處理器

来源:http://www.cnblogs.com/bb-love-dd/archive/2016/10/04/5930169.html
-Advertisement-
Play Games

欲練神功,引刀自宮。為了避免記憶體管理的煩惱,Java咔嚓一下,把指針砍掉了。當年.Net也追隨潮流,咔嚓了一下,化名小桂子,登堂入室進了皇宮。康熙往下麵一抓:咦?還在?——原來是假太監韋小寶。 打開unsafe選項,C 指針就biu的一下子蹦出來了。指針很強大,沒必要拋棄這一強大的工具。誠然,在大多 ...


欲練神功,引刀自宮。為了避免記憶體管理的煩惱,Java咔嚓一下,把指針砍掉了。當年.Net也追隨潮流,咔嚓了一下,化名小桂子,登堂入室進了皇宮。康熙往下麵一抓:咦?還在?——原來是假太監韋小寶。

打開unsafe選項,C#指針就biu的一下子蹦出來了。指針很強大,沒必要拋棄這一強大的工具。誠然,在大多數情況下用不上指針,但在特定的情況下還是需要用到的。比如:

(1)大規模的運算中使用指針來提高性能;

(2)與非托管代碼進行交互;

(3)在實時程式中使用指針,自行管理記憶體和對象的生命周期,以減少GC的負擔。

目前使用指針的主要語言是C和C++。但是由於語法限制,C和C++中的指針的玩法很單調,在C#中,可以進行更優雅更好玩的玩法。本文是《重新認識C#: 玩轉指針》一文的續篇,主要是對《重新認識C#: 玩轉指針》內容進行總結和改進。

C#下使用指針有兩大限制:

(1)使用指針只能操作struct,不能操作class;

(2)不能在泛型類型代碼中使用未定義類型的指針。

第一個限制沒辦法突破,因此需要將指針操作的類型設為struct。struct + 指針,恩,就把C#當更好的C來用吧。對於第二個限制,寫一個預處理器來解決問題。

下麵是我寫的簡單的C#預處理器的代碼,不到200行:
1 using System;
2 using System.Collections.Generic;
3 using System.IO;
4 using System.Text;
5 using System.Text.RegularExpressions;
6
7 namespace Orc.Util.Csmacro
8 {
9 class Program
10 {
11 static Regex includeReg = new Regex("#region\s+include.+\s+#endregion");
12 static Regex mixinReg = new Regex("(?<=#region\s+mixin\s)[\s|\S]+(?=#endregion)");
13 ///
14 /// Csmacro [dir|filePath]
15 ///
16 /// 語法:
17 /// #region include ""
18 /// #endregion
19 ///
20 ///
21 ///
22 static void
23 #region include<>
24 Main
25 #endregion
26 (string[] args)
27 {
28 if (args.Length != 1)
29 {
30 PrintHelp();
31 return;
32 }
33
34 String filePath = args[0];
35
36 Path.GetDirectoryName(filePath);
37 String dirName = Path.GetDirectoryName(filePath);
38 #if DEBUG
39 Console.WriteLine("dir:" + dirName);
40 #endif
41 String fileName = Path.GetFileName(filePath);
42 #if DEBUG
43 Console.WriteLine("file:" + fileName);
44 #endif
45
46 if (String.IsNullOrEmpty(fileName))
47 {
48 Csmacro(new DirectoryInfo(dirName));
49 }
50 else
51 {
52 if (fileName.EndsWith(".cs") == false)
53 {
54 Console.WriteLine("Csmacro只能處理尾碼為.cs的源程式.");
55 }
56 else
57 {
58 Csmacro(new FileInfo(fileName));
59 }
60 }
61
62 Console.WriteLine("[Csmacro]:處理完畢.");
63
64 #if DEBUG
65 Console.ReadKey();
66 #endif
67 }
68
69 static void Csmacro(DirectoryInfo di)
70 {
71 Console.WriteLine("[Csmacro]:進入目錄" + di.FullName);
72
73 foreach (FileInfo fi in di.GetFiles("*.cs", SearchOption.AllDirectories))
74 {
75 Csmacro(fi);
76 }
77 }
78
79 static void Csmacro(FileInfo fi)
80 {
81 String fullName = fi.FullName;
82 if (fi.Exists == false)
83 {
84 Console.WriteLine("[Csmacro]:文件不存在-" + fullName);
85 }
86 else if (fullName.EndsWith("_Csmacro.cs"))
87 {
88 return;
89 }
90 else
91 {
92 String text = File.ReadAllText(fullName);
93
94 DirectoryInfo parrentDirInfo = fi.Directory;
95
96 MatchCollection mc = includeReg.Matches(text);
97 if (mc == null || mc.Count == 0) return;
98 else
99 {
100 Console.WriteLine("[Csmacro]:處理文件" + fullName);
101
102 StringBuilder sb = new StringBuilder();
103
104 Int32 from = 0;
105 foreach (Match item in mc)
106 {
107 sb.Append(text.Substring(from, item.Index - from));
108 from = item.Index + item.Length;
109 sb.Append(Csmacro(parrentDirInfo, item.Value));
110 }
111
112 sb.Append(text.Substring(from, text.Length - from));
113
114 String newName = fullName.Substring(0, fullName.Length - 3) + "_Csmacro.cs";
115 if (File.Exists(newName))
116 {
117 Console.WriteLine("[Csmacro]:刪除舊文件" + newName);
118 }
119 File.WriteAllText(newName, sb.ToString());
120 Console.WriteLine("[Csmacro]:生成文件" + newName);
121 }
122 }
123 }
124
125 static String Csmacro(DirectoryInfo currentDirInfo, String text)
126 {
127 String outfilePath = text.Replace("#region", String.Empty).Replace("#endregion", String.Empty).Replace("include",String.Empty).Replace(""",String.Empty).Trim();
128 try
129 {
130 if (Path.IsPathRooted(outfilePath) == false)
131 {
132 outfilePath = currentDirInfo.FullName + @"" + outfilePath;
133 }
134 FileInfo fi = new FileInfo(outfilePath);
135 if (fi.Exists == false)
136 {
137 Console.WriteLine("[Csmacro]:文件" + fi.FullName + "不存在.");
138 return text;
139 }
140 else
141 {
142 return GetMixinCode(File.ReadAllText(fi.FullName));
143 }
144 }
145 catch (Exception ex)
146 {
147 Console.WriteLine("[Csmacro]:出現錯誤(" + outfilePath + ")-" + ex.Message);
148 }
149 finally
150 {
151 }
152 return text;
153 }
154
155 static String GetMixinCode(String txt)
156 {
157 Match m = mixinReg.Match(txt);
158 if (m.Success == true)
159 {
160 return m.Value;
161 }
162 else return String.Empty;
163 }
164
165 static void PrintHelp()
166 {
167 Console.WriteLine("Csmacro [dir|filePath]");
168 }
169 }
170 }

然後編譯為 Csmacro.exe ,放入系統路徑下。在需要使用預處理器的項目中添加 Pre-build event command lind:

Csmacro.exe $(ProjectDir)

Visual Studio 有個很好用的關鍵字 “region” ,我們就把它當作我們預處理器的關鍵字。include 一個文件的語法是:

region include "xxx.cs"

endregion

一個文件中可以有多個 #region include 塊。

被引用的文件不能全部引用,因為一個C#文件中一般包含有 using,namespace … 等,全部引用的話會報編譯錯誤。因此,在被引用文件中,需要通過關鍵字來規定被引用的內容:

region mixin

endregion

這個預處理器比較簡單。被引用的文件中只能存在一個 #region mixin 塊,且在這個region的內部,不能有其它的region塊。

預處理器 Csmacro.exe 的作用就是找到所有 cs 文件中的 #region include 塊,根據 #region include 路徑找到被引用文件,將該文件中的 #region mixin 塊 取出,替換進 #region include 塊中,生成一個以_Csmacro.cs結尾的新文件 。

由於C#的兩個語法糖“partial” 和 “using”,預處理器非常好用。如果沒有這兩個語法糖,預處理器會很醜陋不堪。(誰說語法糖沒價值!一些小小的語法糖,足以實現新的編程範式。)

partial 關鍵字 可以保證一個類型的代碼存在幾個不同的源文件中,這保證了預處理器的執行,您可以像寫正常的代碼一樣編寫公共部分代碼,並且正常編譯。

using 關鍵字可以為類型指定一個的別名。這是一個不起眼的語法糖,卻在本文中非常重要:它可以為不同的類型指定一個相同的類型別名。之所以引入預處理器,就是為了復用包含指針的代碼。我們可以將代碼抽象成兩部分:變化部分和不變部分。一般來說,變化部分是類型的型別,如果還有其它非類型的變化,我們也可以將這些變化封裝成新的類型。這樣一來,我們可以將變化的類型放在源文件的頂端,使用using 關鍵字,命名為固定的別名。然後把不變部分的代碼,放在 #region mixin 塊中。這樣的話,讓我們需要 #region include 時,只需要在 #region include 塊的前面(需要在namespace {} 的外部)為類型別名指定新的類型。

舉例說明,點陣圖根據像素的格式可以分為很多種,這裡假設有兩種圖像,一種是像素是一個Byte的灰度圖像ImageU8,一個是像素是一個Argb32的彩色圖像ImageArgb32。ImageU8代碼如下:

1 public class ImageU8
2 {
3 public Int32 Width { get; set; }
4 public Int32 Height { get; set; }
5
6 public unsafe Byte* Pointer;
7 public unsafe void SetValue(Int32 row, Int32 col, Byte value)
8 {
9 Pointer[row * Width + col] = value;
10 }
11 }

在 ImageArgb32 中,我們也要寫重覆的代碼:

1 public class ImageArgb32
2 {
3 public Int32 Width { get; set; }
4 public Int32 Height { get; set; }
5
6 public unsafe Argb32* Pointer;
7 public unsafe void SetValue(Int32 row, Int32 col, Argb32 value)
8 {
9 Pointer[row * Width + col] = value;
10 }
11 }

對於 Width和Height屬性,我們可以建立基類來進行抽象和復用,然而,對於m_pointer和SetValue方法,如果放在基類中,則需要抹去類型信息,且變的十分醜陋。由於C#不支持泛型類型的指針,也無法提取為泛型代碼。

使用 Csmacro.exe 預處理器,我們就可以很好的處理。

首先,建立一個模板文件 Image_Template.cs

代碼

1 using TPixel = System.Byte;
2
3 using System;
4
5 namespace XXX.Hidden
6 {
7 class Image_Template
8 {
9 public Int32 Width { get; set; }
10 public Int32 Height { get; set; }
11
12 #region mixin
13
14 public unsafe TPixel* Pointer;
15 public unsafe void SetValue(Int32 row, Int32 col, TPixel value)
16 {
17 Pointer[row * Width + col] = value;
18 }
19
20 #endregion
21 }
22 }

然後建立一個基類 BaseImage,再從BaseImage派生ImageU8和ImageArgb32。兩個派生類都是 partial 類:

下麵我們建立一個 ImageU8_ClassHelper.cs 文件,來 #region include 引用上面的模板文件:

1 using TPixel = System.Byte;
2
3 using System;
4 namespace XXX
5 {
6 public partial class ImageU8
7 {
8 #region include "Image_Template.cs"
9 #endregion
10 }
11 }

編譯,編譯器會自動生成文件 “ImageU8_ClassHelper_Csmacro.cs” 。將這個文件引入項目中,編譯通過。這個文件內容是:

代碼

1 using TPixel = System.Byte;
2
3 using System;
4 namespace XXX
5 {
6 public partial class ImageU8
7 {
8
9 public unsafe TPixel* Pointer;
10 public unsafe void SetValue(Int32 row, Int32 col, TPixel value)
11 {
12 Pointer[row * Width + col] = value;
13 }
14
15 }
16 }

對於 ImageArgb32 類也可以進行類似操作。

從這個例子可以看出,使用 partial 關鍵字,能夠讓原代碼、模板代碼、ClassHelper代碼三者共存。使用 using 關鍵字,可以分離出代碼中變化的部分出來。

下麵是我寫的圖像操作的一些模板代碼:

(1)通過模板提供指針和索引器:

代碼

1 using TPixel = System.Byte;
2 using TCache = System.Int32;
3 using TKernel = System.Int32;
4
5 using System;
6 using System.Collections.Generic;
7 using System.Text;
8
9 namespace Orc.SmartImage.Hidden
10 {
11 public abstract class Image_Template : UnmanagedImage
12 {
13 private Image_Template()
14 : base(1,1)
15 {
16 throw new NotImplementedException();
17 }
18
19 #region mixin
20
21 public unsafe TPixel* Start { get { return (TPixel)this.StartIntPtr; } }
22
23 public unsafe TPixel this[int index]
24 {
25 get
26 {
27 return Start[index];
28 }
29 set
30 {
31 Start[index] = value;
32 }
33 }
34
35 public unsafe TPixel this[int row, int col]
36 {
37 get
38 {
39 return Start[row * this.Width + col];
40 }
41 set
42 {
43 Start[row * this.Width + col] = value;
44 }
45 }
46
47 public unsafe TPixel
Row(Int32 row)
48 {
49 if (row < 0 || row >= this.Height) throw new ArgumentOutOfRangeException("row");
50 return Start + row * this.Width;
51 }
52
53 #endregion
54 }
55 }

(2)通過模板提供常用的操作和Lambda表達式支持

代碼

1 using TPixel = System.Byte;
2 using TCache = System.Int32;
3 using TKernel = System.Int32;
4
5 using System;
6 using System.Collections.Generic;
7 using System.Text;
8
9 namespace Orc.SmartImage.Hidden
10 {
11 static class ImageClassHelper_Template
12 {
13 #region mixin
14
15 public unsafe delegate void ActionOnPixel(TPixel* p);
16 public unsafe delegate void ActionWithPosition(Int32 row, Int32 column, TPixel* p);
17 public unsafe delegate Boolean PredicateOnPixel(TPixel* p);
18
19 public unsafe static void ForEach(this UnmanagedImage src, ActionOnPixel handler)
20 {
21 TPixel* start = (TPixel)src.StartIntPtr;
22 TPixel
end = start + src.Length;
23 while (start != end)
24 {
25 handler(start);
26 ++start;
27 }
28 }
29
30 public unsafe static void ForEach(this UnmanagedImage src, ActionWithPosition handler)
31 {
32 Int32 width = src.Width;
33 Int32 height = src.Height;
34
35 TPixel* p = (TPixel)src.StartIntPtr;
36 for (Int32 r = 0; r < height; r++)
37 {
38 for (Int32 w = 0; w < width; w++)
39 {
40 handler(w, r, p);
41 p++;
42 }
43 }
44 }
45
46 public unsafe static void ForEach(this UnmanagedImage src, TPixel
start, uint length, ActionOnPixel handler)
47 {
48 TPixel* end = start + src.Length;
49 while (start != end)
50 {
51 handler(start);
52 ++start;
53 }
54 }
55
56 public unsafe static Int32 Count(this UnmanagedImage src, PredicateOnPixel handler)
57 {
58 TPixel* start = (TPixel)src.StartIntPtr;
59 TPixel
end = start + src.Length;
60 Int32 count = 0;
61 while (start != end)
62 {
63 if (handler(start) == true) count++;
64 ++start;
65 }
66 return count;
67 }
68
69 public unsafe static Int32 Count(this UnmanagedImage src, Predicate handler)
70 {
71 TPixel* start = (TPixel)src.StartIntPtr;
72 TPixel
end = start + src.Length;
73 Int32 count = 0;
74 while (start != end)
75 {
76 if (handler(start) == true) count++;
77 ++start;
78 }
79 return count;
80 }
81
82 public unsafe static List Where(this UnmanagedImage src, PredicateOnPixel handler)
83 {
84 List list = new List();
85
86 TPixel
start = (TPixel)src.StartIntPtr;
87 TPixel
end = start + src.Length;
88 while (start != end)
89 {
90 if (handler(start) == true) list.Add(start);
91 ++start;
92 }
93
94 return list;
95 }
96
97 public unsafe static List Where(this UnmanagedImage src, Predicate handler)
98 {
99 List list = new List();
100
101 TPixel
start = (TPixel)src.StartIntPtr;
102 TPixel
end = start + src.Length;
103 while (start != end)
104 {
105 if (handler(start) == true) list.Add(start);
106 ++start;
107 }
108
109 return list;
110 }
111
112 ///
113 /// 查找模板。模板中值代表實際像素值。負數代表任何像素。返回查找得到的像素的左上端點的位置。
114 ///
115 ///
116 ///
117 public static unsafe List<System.Drawing.Point> FindTemplate(this UnmanagedImage src, int[,] template)
118 {
119 List<System.Drawing.Point> finds = new List<System.Drawing.Point>();
120 int tHeight = template.GetUpperBound(0) + 1;
121 int tWidth = template.GetUpperBound(1) + 1;
122 int toWidth = src.Width - tWidth + 1;
123 int toHeight = src.Height - tHeight + 1;
124 int stride = src.Width;
125 TPixel* start = (TPixel)src.SizeOfType;
126 for (int r = 0; r < toHeight; r++)
127 {
128 for (int c = 0; c < toWidth; c++)
129 {
130 TPixel
srcStart = start + r * stride + c;
131 for (int rr = 0; rr < tHeight; rr++)
132 {
133 for (int cc = 0; cc < tWidth; cc++)
134 {
135 int pattern = template[rr, cc];
136 if (pattern >= 0 && srcStart[rr * stride + cc] != pattern)
137 {
138 goto Next;
139 }
140 }
141 }
142
143 finds.Add(new System.Drawing.Point(c, r));
144
145 Next:
146 continue;
147 }
148 }
149
150 return finds;
151 }
152
153 #endregion
154 }
155 }

配合lambda表達式,用起來很爽。在方法“FindTemplate”中,有這一句:

if (pattern >= 0 && srcStart[rr * stride + cc] != pattern)

其中 srcStart[rr * stride + cc] 是 TPixel 不定類型,而 pattern 是 int 類型,兩者之間需要進行比較,但是並不是所有的類型都提供和整數之間的 != 操作符。為此,我建立了個新的模板 TPixel_Template。

(3)通過模板提供 != 操作符 的定義

1 using TPixel = System.Byte;
2 using System;
3
4 namespace Orc.SmartImage.Hidden
5 {
6 public struct TPixel_Template
7 {
8 /*
9 #region mixin
10
11 public static Boolean operator ==(TPixel lhs, int rhs)
12 {
13 throw new NotImplementedException();
14 }
15
16 public static Boolean operator !=(TPixel lhs, int rhs)
17 {
18 throw new NotImplementedException();
19 }
20
21 public static Boolean operator ==(TPixel lhs, double rhs)
22 {
23 throw new NotImplementedException();
24 }
25
26 public static Boolean operator !=(TPixel lhs, double rhs)
27 {
28 throw new NotImplementedException();
29 }
30
31 public static Boolean operator ==(TPixel lhs, float rhs)
32 {
33 throw new NotImplementedException();
34 }
35
36 public static Boolean operator !=(TPixel lhs, float rhs)
37 {
38 throw new NotImplementedException();
39 }
40
41 #endregion
42
43 */
44 }
45 }

這裡,在 #region mixin 塊被註釋掉了,不註釋掉編譯器會報錯。註釋之後,不會影響程式預處理。

通過 ClassHelper類來使用模板:

代碼

1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4
5 namespace Orc.SmartImage
6 {
7 using TPixel = Argb32;
8 using TCache = System.Int32;
9 using TKernel = System.Int32;
10
11 public static partial class ImageArgb32ClassHelper
12 {
13 #region include "ImageClassHelper_Template.cs"
14 #endregion
15 }
16
17 public partial class ImageArgb32
18 {
19 #region include "Image_Template.cs"
20 #endregion
21 }
22
23 public partial struct Argb32
24 {
25 #region include "TPixel_Template.cs"
26 #endregion
27 }
28 }

由於 Argb32 未提供和 int 之間的比較,因此,在這裡 #region include "TPixel_Template.cs"。而Byte可以與int比較,因此,在ImageU8中,就不需要#region include "TPixel_Template.cs":

3 using System;
4 using System.Collections.Generic;
5 using System.Text;
6
7 namespace Orc.SmartImage
8 {
9 using TPixel = System.Byte;
10 using TCache = System.Int32;
11 using TKernel = System.Int32;
12
13 public static partial class ImageU8ClassHelper
14 {
15 #region include "ImageClassHelper_Template.cs"
16 #endregion
17 }
18
19 public partial class ImageU8
20 {
21 #region include "Image_Template.cs"
22 #endregion
23 }
24 }

是不是很有意思呢?強大的指針,結合C#強大的語法和快速編譯,至少在圖像處理這一塊是很好用的。

 預處理器指令的開頭都有符號#。

  1. define 和 #undef

  #define 的用法如下所示: #define DEBUG

  它告訴編譯器存在給定名稱的符號,在本例中是DEBUG。這有點類似於聲明一個變數,但這個變數並沒有真正的值,只是存在而已。

  這個符號不是實際代碼的一部分,而只在編譯器編譯代碼時存在。在C#代碼中它沒有任何意義。

  #undef 正好相反—— 它刪除符號的定義: #undef DEBUG

  如果符號不存在,#undef 就沒有任何作用。同樣,如果符號已經存在,則#define 也不起作用。必須把#define 和#undef 命令放在C#源文件的開頭位置,在聲明要編譯的任何對象的代碼之前。

  #define 本身並沒有什麼用,但與其他預處理器指令(特別是#if)結合使用時,它的功能就非常強大了。

  這裡應註意一般C#語法的一些變化。預處理器指令不用分號結束,一般一行上只有一條命令。這是因為對於預處理器指令,C#不再要求命令使用分號進行分隔。如果它遇到一條預處理器指令,就會假定下一條命令在下一行上。

  1. if、#elif、#else 和#endif

  這些指令告訴編譯器是否要編譯某個代碼塊。考慮下麵的方法:

1 int DoSomeWork(double x)
2 {
3 // do something
4 #if DEBUG
5 Console.WriteLine("x is " + x);
6 #endif
7 }

  這段代碼會像往常那樣編譯,但Console.WriteLine 命令包含在#if 子句內。

  這行代碼只有在前面的#define 命令定義了符號DEBUG 後才執行。

  當編譯器遇到#if 語句後,將先檢查相關的符號是否存在,如果符號存在,就編譯#if 子句中的代碼。否則,編譯器會忽略所有的代碼,直到遇到匹配的#endif 指令為止。

  一般是在調試時定義符號DEBUG,把與調試相關的代碼放在#if 子句中。在完成了調試後,就把#define 語句註釋掉,所有的調試代碼會奇跡般地消失,可執行文件也會變小,最終用戶不會被這些調試信息弄糊塗(顯然,要做更多的測試,確保代碼在沒有定義DEBUG 的情況下也能工作)。

  這項技術在C 和C++編程中十分常見,稱為條件編譯(conditional compilation)。

  #elif (=else if)和#else 指令可以用在#if 塊中,其含義非常直觀。也可以嵌套#if 塊:

define ENTERPRISE

define W2K

// further on in the file

if ENTERPRISE

// do something

if W2K

// some code that is only relevant to enterprise
// edition running on W2K

endif

elif PROFESSIONAL

// do something else

else

// code for the leaner version

endif

與C++中的情況不同,使用#if 不是有條件地編譯代碼的唯一方式,C#還通過Conditional 特性提供了另一種機制。
  #if 和#elif 還支持一組邏輯運算符“!”、“==”、“!=”和“||”。如果符號存在,就被認為是true,否則為false,例如:

1 #if W2K && (ENTERPRISE==false) // if W2K is defined but ENTERPRISE isn't

  1. warning 和 #error

  另兩個非常有用的預處理器指令是#warning 和#error,當編譯器遇到它們時,會分別產生警告或錯誤。如果編譯器遇到#warning 指令,會給用戶顯示#warning 指令後面的文本,之後編譯繼續進行。如果編譯器遇到#error 指令,就會給用戶顯示後面的文本,作為一條編譯錯誤消息,然後會立即退出編譯,不會生成IL 代碼。

  使用這兩條指令可以檢查#define 語句是不是做錯了什麼事,使用#warning 語句可以提醒自己執行某個操作:

1 #if DEBUG && RELEASE
2 #error "You've defined DEBUG and RELEASE simultaneously!"
3 #endif
4 #warning "Don't forget to remove this line before the boss tests the code!"
5 Console.WriteLine("I hate this job.");

  1. region 和#endregion

  #region 和#endregion 指令用於把一段代碼標記為有給定名稱的一個塊,如下所示。

1 #region Member Field Declarations
2 int x;
3 double d;
4 Currency balance;
5 #endregion

  這看起來似乎沒有什麼用,它不影響編譯過程。這些指令的優點是它們可以被某些編輯器識別,包括Visual Studio .NET 編輯器。這些編輯器可以使用這些指令使代碼在屏幕上更好地佈局。

  1. line

  #line 指令可以用於改變編譯器在警告和錯誤信息中顯示的文件名和行號信息。這條指令用得並不多。

  如果編寫代碼時,在把代碼發送給編譯器前,要使用某些軟體包改變輸入的代碼,就可以使用這個指令,因為這意味著編譯器報告的行號或文件名與文件中的行號或編輯的文件名不匹配。

  #line 指令可以用於還原這種匹配。也可以使用語法#line default 把行號還原為預設的行號:

1 #line 164 "Core.cs" // We happen to know this is line 164 in the file
2 // Core.cs, before the intermediate
3 // package mangles it.
4 // later on
5 #line default // restores default line numbering

  1. pragma

  #pragma 指令可以抑制或還原指定的編譯警告。與命令行選項不同,#pragma 指令可以在類或方法級別執行,對抑制警告的內容和抑制的時間進行更精細的控制。

  下麵的例子禁止“欄位未使用”警告,然後在編譯MyClass 類後還原該警告。

1 #pragma warning disable 169
2 public class MyClass
3 {
4 int neverUsedField;
5 }
6 #pragma warning restore 169


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、背景 在之前做的小項目里有一需求是:要求將一活動錄入的數據進行統計,並以excel表格形式導出來,並且對錶格格式要求並不高。 二、問題分析 鑒於用戶只要求最終將資料庫中的數據導出excel,對於格式要求不高,因此只需要在頁面上加入一條鏈接,後臺action中讀取數據然後通過第三方組件導出,再下載 ...
  • 本人最近想做個桌面應用,參考了一下時下流行的各個軟體,發現大部分軟體的標題欄都是自定義的,甚至沒有標題欄,整個視窗為一個整體。 從整體感來說,預設的標題欄非常的破壞軟體風格的一致性,尤其是設置背景圖時,標題欄的顏色會顯得很礙眼。 所以,找了一些大神分享的技術貼,也同時給大家分享下我的心得。 參考鏈接 ...
  • 這篇文章主要講述 IIS 8 部署免費 HTTPS 。 HTTPS 是互聯網 web 大勢所趨。TaSaid 最近把機房從香港遷移到青島,趁著這次機會,觀望並折騰了幾天,在遷移中順便完成了 HTTPS 的部署。 ...
  • 剛接到這樣的任務時,沒有感覺到任何壓力,不就是給移動端應用提供數據嗎?那邊發來參數,這邊處理數據,返回JSON。做網站開發時經常使用ajax請求後臺數據,不就是這麼回事嗎。於是,在確認完需求後就開始幹了,很快,進入聯調階段,這個時候各種問題來了,忙得不可開交。吃一塹,長一智,項目結束後總結了下,大致 ...
  • 虛方法和抽象方法都可以供派生類重寫,它們之間有什麼區別呢? 1. 虛方法必須有實現部分,併為派生類提供了覆蓋該方法的選項 抽象方法沒有提供實現部分,抽象方法是一種強制派生類覆蓋的方法,否則派生類將不能被實例化。如: //抽象方法 public abstract class Animal { publ ...
  • 一、獲取文件夾列表 /// /// 獲取文件夾下的文件列表 /// /// string Path:文件夾路徑(@"C:\") /// string SearchPattern:擴展名過濾(" .txt") /// bool SearchChild:為False不搜索子目錄,為True搜索子目錄 / ...
  • 在asp.net mvc項目里,用戶需要開拓幾個活動版面,並以側欄的方式呈現在首頁右側,幾個活動時間不一致,為避免瀏覽者在活動未開放之時進入未開放的服務頁面。因此不僅需要在活動代碼中加入限制功能,也需要在前臺取消不合時宜的頁面的展示。 ...
  • 第一部分:程式集(System.Reflection.Assembly) 1.獲取Assembly對象 方法1:調用Assembly的以下4個靜態方法Get...()之一: GetAssembly(Type t) GetCallingAssembly() ——返回調用當前方法的方法所在的程式集 Ge ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...