1_預處理指令
(1)源代碼指定了程序的定義,預處理指令(preprocessor directive)指示編譯器如何處理源代碼。例如,在某些情況下,我們希望編譯器能夠忽略一部分代碼,而在其他情況下,我們希望代碼被編譯,這時我們就可以使用預處理指令了。
(2)基本規則
-
預處理指令必須和C#代碼在不同的行
-
與C#語句不同,預處理指令不需要以分號結尾
-
包含預處理指令的每一行必須與 ‘’#‘’ 字符開始(在#字符前可以有空格,在#字符和指令之間也可以有空格)
-
允許行尾注釋
-
在預處理指令所在的行不允許有分隔符注釋
(3)C# 預處理器指令列表
預處理器指令 | 描述 |
---|---|
#define | 它用于定義一系列成為符號的字符。 |
#undef | 它用于取消定義符號。 |
#if | 它用于測試符號是否為真 |
#else | 它用于創建復合條件指令,與 #if 一起使用。 |
#elif | 它用于創建復合條件指令。 |
#endif | 指定一個條件指令的結束。 |
#line | 它可以讓您修改編譯器的行數以及(可選地)輸出錯誤和警告的文件名。 |
#error | 它允許從代碼的指定位置生成一個錯誤。 |
#warning | 它允許從代碼的指定位置生成一級警告。 |
#region | 它可以讓您在使用 Visual Studio Code Editor 的大綱特性時,指定一個可展開或折疊的代碼塊。 |
#endregion | 它標識著 #region 塊的結束。 |
#define Debug //定義一個編譯符號類似于定義一個變量,#define 聲明,c#中的預處理指令
#define Log
#undef Log
static void Main(string[] args)
{
#if Log //當存在Log的時候執行1,后續不執行Console.WriteLine("執行了");//執行1
#elif Debug //當Log不存在,Debug存在時執行2,后續不執行Console.WriteLine("Debug");//執行2
#else ?//當上述條件都不滿足時執行Console.WriteLine("world");//執行3
#endif//上述相當于if elseif ...else
}
2_特性
2.1_特性的定義
(1)特性(Attribute)是用于在運行時傳遞程序中各種元素(比如類、方法、結構、枚舉、組件等)的行為信息的聲明性標簽。可以通過使用特性向程序添加聲明性信息。一個聲明性標簽是通過放置在它所應用的元素前面的方括號([ ])來描述的。
特性(Attribute)用于添加元數據,如編譯器指令和注釋、描述、方法、類等其他信息。.Net 框架提供了兩種類型的特性:預定義特性和自定義特性。
特性(Attribute)是一種可由用戶自有定義的修飾符(Modifier),可以用來修飾各種需要被修飾的目標。我們可以對類、以及C#程序集中的成員進行進一步的描述。
簡單地說,Attribute的作用是為它們的附著體追加上一些額外的信息(這些信息保存在附著物的體內)——比如“這個類是我寫的”或者“這個函數以前出過問題”等等
(2)元數據:保存在程序集中有關程序及其類型的數據。元數據主要用來描述C#中各種元素(類,方法,構造函數,屬性等)。
(3)Attribute與注釋的區別
注釋是對程序源代碼的一種說明,主要目的是給人看的,在程序被編譯的時候會被編譯器所丟棄,因此,它絲毫不會影響到程序的執行。
Attribute是程序代碼的一部分,它不但不會被編譯器丟棄,而且還會被編譯器編譯進程序集(Assembly)的元數據(Metadata)里。在程序運行的時候,隨時可以從元數據中提取出這些附加信息,并以之決策程序的運行。
元數據:.NET中元數據是指程序集中的命名空間、類、方法、屬性等信息,這些信息是可以通過Reflection讀取出來的。
(4)常用特性:
AttributeUsage,Conditional,Obsolete,Category , Description , Browsable , DefaultValue ,Serializable
2.2_三種預定義特性分類
2.2.1_Conditional條件特性
這個預定義特性標記了一個條件方法,其執行依賴于指定的預處理標識符。它會引起方法調用的條件編譯,取決于指定的值,比如 Debug 或 Trace。例如,當調試代碼時顯示變量的值。
Conditional條件特性 應用在方法上,讓方法按照條件執行 全稱:ConditionalAttribute
#define Debug
static void Main(string[] args)
{Test1();Test2();
}
public static void Test1()
{Console.WriteLine("Test1");
}
//Conditional條件特性 應用在方法上,讓方法按照條件執行 全稱:ConditionalAttribute
//Debug就是一個編譯符號,當定義了這個編譯符號,才會編譯Test2;
[Conditional("Debug")]
public static void Test2()
{Console.WriteLine("Test2");
}
2.2.2_Obsolete廢棄特性
這個預定義特性標記了不應被使用的程序實體。它可以讓您通知編譯器丟棄某個特定的目標元素。例如,當一個新方法被用在一個類中,但是您仍然想要保持類中的舊方法,您可以通過顯示一個應該使用新方法,而不是舊方法的消息,來把它標記為 obsolete(過時的)。
-
參數 message,是一個字符串,描述項目為什么過時以及該替代使用什么。
-
參數 iserror,是一個布爾值。如果該值為 true,編譯器應把該項目的使用當作一個錯誤。默認值是 false(編譯器生成一個警告)。
[Obsolete(message)]
[Obsolete(message,iserror)]
static void Main(string[] args)
{Test1();Test2();
}
//Obsolete 參數1:提示信息,參數2 bool值 為true的時候,代表此方法不可用
[Obsolete("這個方法已經被棄用,可以使用Test2方法代替",true)]//這個方法已經被棄用,可以使用Test2方法代替
public static void Test1()//1.0
{Console.WriteLine("Test1");
}
public static void Test2()//2.0
{Console.WriteLine("Test2");
}
2.2.3_AttributeUsage預定義特性
預定義特性 AttributeUsage 描述了如何使用一個自定義特性。它規定了特性可應用到的項目的類型。
-
參數 validon 規定特性可被放置的語言元素。它是枚舉器 AttributeTargets 的值的組合。默認值是 AttributeTargets.All。
-
參數 AllowMultiple(可選的)為該特性的 AllowMultiple屬性(property)提供一個布爾值。如果為 true,則該特性是多用的。默認值是 false(單用的)。
-
參數 inherited(可選的)為該特性的 Inherited 屬性(property)提供一個布爾值。如果為 true,則該特性可被派生類繼承。默認值是 false(不被繼承)。
[AttributeUsage(validon,AllowMultiple=allowmultiple,Inherited=inherited)]
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
2.2.4_自定義特性
(1)特性本質上就是一個類:
-
命名建議以Attribute結尾,建議使用大駝峰。
-
必須繼承基類Attribute
-
使用AttributeUsage特性來控制自定義的特性的應用范圍。
(2)小技巧:怎么判斷對象是一個特性呢?看對象的結尾是否以Attribute結尾,只要以Attribute結尾的基本上是特性。 特性在定義時,建議以Attribute結尾。特性肯定是一個類,必須繼承Attribute。Atribute是特性的基類。
(3)自定義一個特性
//AttributeUsage也重載了|運算符
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Method,AllowMultiple =true)]
public ?class MyAttribute:Attribute
{/// <summary>/// 版本,提示,創建時間/// </summary>public string Version { get; set; }public string Message { get; set; }public string CallTime { get; set; }public MyAttribute(string version,string message,string callTime) {Version= version;Message= message;CallTime= callTime;}
}
(4)自定義特性的使用
[MyAttribute("1.0","張三","2025-08-11")]
internal class Program
{//自定義特性可以簡寫// [My("1.0", "張三", "2025-08-11")]public string id { get; set; }static void Main(string[] args){Test();//原始方法//反射預熱//通過反射獲取Type t1 = typeof(Program);//拿到Program類的類型,因為Test在ProgramConsole.WriteLine(t1);MethodInfo mi=t1.GetMethod("Test");//拿到Test方法//mi不能使用()執行,需要使用Invoke調用mi.Invoke(null,null);//獲取test1上使用的特性//參數1:特性的類型//參數2:是否支持繼承搜索object[] attrs = mi.GetCustomAttributes(typeof(MyAttribute),false);foreach (MyAttribute attr in attrs){Console.WriteLine(attr.Version);}Console.WriteLine(attrs);}[My("1.0", "張三", "2025-08-11")][My("2.0", "李斯", "2025-08-11")]public static void Test(){Console.WriteLine("方法1");}public static void Test2(){}public static void Test3(){}
}
2.2.5_其他特性:應用于自定義控件中:
(1)特性:
-
Description特性用來給屬性和事件添加描述信息(解釋說明)
-
Category 特性用來給屬性分類 默認分類放到"雜項"
-
Browsable 特性用來控制屬性是否在屬性窗口中出現
-
DefaultValue 特性 設置默認值 在 Visual Studio 的屬性窗口中,如果屬性值等于默認值,屬性值會顯示為普通文本;如果被修改過,則會顯示為粗體。
(2)多個特性的時候,可以用英文逗號分割
//自定義控件,繼承自UserControl用戶空間
public partial class UserControl1 : UserControl{public UserControl1(){InitializeComponent();}//Description特性用來給屬性和事件添加描述信息(解釋說明)//Category 特性用來給屬性分類 默認分類放到"雜項"//Browsable 特性用來控制屬性是否在屬性窗口中出現// DefaultValue 特性 設置默認值 在 Visual Studio 的屬性窗口中,如果屬性值等于默認值,屬性值會顯示為普通文本;如果被修改過,則會顯示為粗體。[Description("控制賬號信息")][Category("吳亦凡")][Browsable(true)][DefaultValue("吳亦凡的賬號")]public string Account{get { return textBox1.Text; }set { textBox1.Text = value; }}//多個特性的時候,可以用英文逗號分割[Description("控制密碼信息"),Category("吳亦凡"),Obsolete("不建議使用此屬性")]public string Password{get { return textBox2.Text; }set { textBox2.Text = value; }}}
3_反射
3.1_反射的定義
(1)反射是指在程序運行中,查看、操作其他程序集或者自身的元數據的各種信息(類、方法,屬性、變量、對象等)的行為。C#中的反射(Reflection)是一種強大的功能,允許你在運行時檢查和操作程序集、類型和對象的信息,基本上,使用反射可以在代碼運行時檢查和操作指定的類及其成員。C#反射的原理主要基于元數據(與C#特性相似),即程序集中存儲的有關類型、方法等的信息。因為反射可以在程序編譯后獲得信息,所以它提高了程序的拓展性和靈活性。
反射就是為了拿到各種元素對應的標簽。Reflection反射。使用反射時,代碼性能低,因為反射使用了裝箱和拆箱。
(2)優缺點
優點:
-
反射提高了程序的靈活性和擴展性。
-
降低耦合性,提高自適應能力。
-
它允許程序創建和控制任何類的對象,無需提前硬編碼目標類。
缺點:
-
性能問題:使用反射基本上是一種解釋操作,用于字段和方法接入時要遠慢于直接代碼。因此反射機制主要應用在對靈活性和拓展性要求很高的系統框架上,普通程序不建議使用。
-
使用反射會模糊程序內部邏輯;程序員希望在源代碼中看到程序的邏輯,反射卻繞過了源代碼的技術,因而會帶來維護的問題,反射代碼比相應的直接代碼更復雜。
(3)反射的作用
反射(Reflection)有下列用途:(可以讓查看或使用程序集以及程序集中的對象多了一種方法)
-
它允許在運行時查看特性(attribute)信息。
-
它允許審查集合中的各種類型,以及實例化這些類型。
-
它允許延遲綁定的方法和屬性(property)。
-
它允許在運行時創建新類型,然后使用這些類型執行一些任務。
3.2_反射示例
(1)沒有反射之前:查看或使用程序集以及程序集中的對象方法:1.引用程序集 2.引入命名空間,3.實例化
(2)有反射后:反射通過元數據獲取程序集或者程序集的對象信息;主要信息:類型,成員(字段 屬性 方法)...
(3)Type類:類型聲明的類,
屬性 | 說明 |
---|---|
Name屬性 | 類型名稱 |
Namespace屬性 | 類所在的命名空間 |
FullName屬性 | 類所在的命名空間+類名 |
GetField()方法 | 獲取指定名稱的公共字段 |
GetFields()方法 | 獲取所有的公共字段 |
GetProperty()方法 | 獲取指定名稱的公共屬性 |
GetProperties()方法 | 獲取所有的公共屬性 |
GetMethod()方法 | 獲取指定名稱的公共方法 |
GetMethods()方法 | 獲取所有的公共方法 |
GetNestedType() | 獲取指定名稱的嵌套類型(類中的類) |
GetConstructor() | 獲取類的構造函數 |
GetEvent() | 獲取類的事件 |
GetCustomAttribute() | 獲取類的特性 |
typeof() 和 GetType() | /獲取類型 |
Invoke() 和 InvokeMember() | 調用相應的成員 |
SetValue(),GetValue() | 設置屬性,獲取屬性 |
typeof():獲取參數的數據類型
Type type1=typeof(string);
Type type2=typeof(int);
Type type3=typeof(Student);
Console.WriteLine(type1);
Console.WriteLine(type2);
Console.WriteLine(type3);
Student student=new Student();//沒有使用反射,直接創建實例
Type type4 = student.GetType();
Console.WriteLine(type4);
Console.WriteLine(type1.Name);//類型名稱:string
Console.WriteLine(type4.Name);//Studetn
Console.WriteLine(type4.Namespace);//類所在的命名空間
Console.WriteLine(type4.FullName);//類所在的命名空間+類名
(5)Assembly類:表示一個程序集
方法 | 說明 |
---|---|
Load | 通過給定程序集的名稱加載程序集 |
LoadFile | 加載指定路徑上的程序集 |
LoadFrom | 已知程序集得文件名或路徑,加載程序集 |
CreateInstance | 從程序集中加載指定得類型,并創建實例,返回值object類型 |
(4)通過反射獲取信息的步驟
獲取程序集——》創建類的實例——》獲取實例的類型——》通過Type的方法獲取類的成員
//2.通過反射獲取字段,屬性,方法
//通過反射創建實例,Load()加載程序集
//如果加載的程序集在當前項目中的bin/DeBug中不存在,會加載失敗
Assembly assembly = Assembly.Load("03_反射");
//CreateInstance()創建,在()里面傳入完整的對象名稱,用加載得程序集創建一個程序集中得對象得實例
//使用反射基礎創建程序集中得某個類得實例
object t1 =assembly.CreateInstance("_03_反射.Student");//類似于new一個實例對象//類似于Student student=new Student();//獲取字段
FieldInfo fieldInfo = type3.GetField("MyId");//獲取公開的字段
fieldInfo.SetValue(t1, 12);
int myId=(int)fieldInfo.GetValue(t1);
Console.WriteLine(myId);
Console.WriteLine("=========獲取所有的公共的字段============");
//獲取所有的字段
FieldInfo[] fieldInfos= type4.GetFields();
foreach(var item in fieldInfos)
{Console.WriteLine(item.Name);
}
Console.WriteLine("----------獲單個屬性----------");
PropertyInfo propertyInfo1= type3.GetProperty("Name");
propertyInfo1.SetValue(t1, "吳亦凡");
string name = (string)propertyInfo1.GetValue(t1);
Console.WriteLine(name);
Console.WriteLine("----------獲取所有公開屬性----------");
PropertyInfo [] propertyInfos = type3.GetProperties();
foreach (var item in propertyInfos)
{Console.WriteLine(item);if (item.Name=="Name"){item.SetValue(t1, "羅志祥");}
}
Console.WriteLine("--------獲取方法-------");
MethodInfo methodInfo= type3.GetMethod("Method2");
//注意:通過數組的形式傳入參數
methodInfo.Invoke(t1, new object[] { "a123","b123"});
MethodInfo []methodInfos= type3.GetMethods();Type s1= type3.GetNestedType("SamllStudent");//獲取Student類下的SamllStudent
object s12 = assembly.CreateInstance(s1.FullName);
4_加密和解密
(1)加密算法分類:對稱加密,非對稱加密,散列算法加密。 對稱加密:使用相同的密鑰對數據進行加密和解密。如:DES算法、AES算法 非對稱加密:使用一對公鑰和私鑰來進行加密和解密。如:RSA算法 散列算法加密:任意長度的數據轉換為固定長度的哈希值。如:MD5算法、SHA算法
公鑰:主要用來把明文加密成密文,用到key。 私鑰:主要用來把密文解密成明文,用到的key。
(2)常用加密算法了解: C#中的加密解密方法:對稱、非對稱與散列算法詳解-CSDN博客
MD5和DES加密及解密封裝參考: C#:使用MD5對用戶密碼加密與解密 - Healer2047 - 博客園
(3)將加密和解密的類封裝,在使用的時候直接調用。
文件地址:"D:\上位機\擴展\EncryptTool.dll"
5_單例模式
什么是設計模式?
設計模式(Design pattern) 是解決軟件開發某些特定問題而提出的一些解決方案也可以理解成解決問題的一些思路。通過設計模式可以幫助我們增強代碼的可重用性、可擴充性、 可維護性、靈活性好。我們使用設計模式最終的目的是實現代碼的 高內聚 和 低耦合。
什么是高內聚和低耦合?
舉例一個現實生活中的例子,例如一個公司,一般都是各個部門各司其職,互不干涉。各個部門需要溝通時通過專門的負責人進行對接。在軟件里面也是一樣的 一個功能模塊只是關注一個功能,一個模塊最好只實現一個功能。這個是所謂的內聚,模塊與模塊之間、系統與系統之間的交互,是不可避免的, 但是我們要盡量減少由于交互引起的單個模塊無法獨立使用或者無法移植的情況發生, 盡可能多的單獨提供接口用于對外操作, 這個就是所謂的低耦合
什么是單例模式?
從“單例”字面意思上理解為一個類只有一個實例,所以單例模式也就是保證一個類只有一個實例的一種實現方法(設計模式其實就是幫助我們解決實際開發過程中的方法, 該方法是為了降低對象之間的耦合度,然而解決方法有很多種,所以前人就總結了一些常用的解決方法為書籍,從而把這本書就稱為設計模式),
官方定義:確保一個類只有一個實例,并提供一個全局訪問點。
為什么會有單例模式?它在什么情況下使用的?
從單例模式的定義中我們可以看出:單例模式的使用自然是當我們的系統中某個對象只需要一個實例的情況,例如:操作系統中只能有一個任務管理器,操作文件時,同一時間內只允許一個實例對其操作等,既然現實生活中有這樣的應用場景,自然在軟件設計領域必須有這樣的解決方案了(因為軟件設計也是現實生活中的抽象),所以也就有了單例模式了。
單例模式和靜態類的區別?
-
首先單例模式會提供給你一個全局唯一的對象,靜態類只是提供給你很多靜態方法,這些方法不用創建對象,通過類就可以直接調用;
-
單例模式的靈活性更高,方法可以被override,因為靜態類都是靜態方法,所以不能被override;
-
如果是一個非常大的對象,單例模式可以懶加載,靜態類就無法做到
那么什么時候時候應該用靜態類,什么時候應該用單例模式呢?首先如果你只是想使用一些工具方法,那么最好用靜態類,靜態類比單例類更快,因為靜態的綁定是在編譯期進行的。如果你要維護狀態信息,或者訪問資源時,應該選用單例模式。還可以這樣說,當你需要面向對象的能力時(比如繼承、多態)時,選用單例類,當你僅僅是提供一些方法時選用靜態類。