0610_特性和反射_加密和解密_单例模式

发布于:2025-06-11 ⋅ 阅读:(22) ⋅ 点赞:(0)

预处理指令

预处理器指令(Preprocessor Directives)指导编译器在实际编译开始之前对信息进行预处理。

#define 定义一个符号,可以用于条件编译。
#undef 取消定义一个符号。
#if 开始一个条件编译块,如果符号被定义则包含代码块。
#elif 如果前面的 #if#elif 条件不满足,且当前条件满足,则包含代码块。
#else 如果前面的 #if#elif 条件不满足,则包含代码块。
#endif 结束一个条件编译块
#warning 生成编译器警告信息。
#error 生成编译器错误信息。
#region 标记一段代码区域,可以在IDE中折叠和展开这段代码,便于代码的组织和阅读。
#endregion 结束一个代码区域。
#line 更改编译器输出中的行号和文件名,可以用于调试或生成工具的代码。
#pragma 用于给编译器发送特殊指令,例如禁用或恢复特定的警告。
#nullable 控制可空性上下文和注释,允许启用或禁用对可空引用类型的编译器检查。

特性(Attribute)

特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的。

特性(Attribute)用于添加元数据,如编译器指令和注释、描述、方法、类等其他信息。.Net 框架提供了两种类型的特性:预定义特性和自定义特性。

Attribute是一种可由用户自有定义的修饰符(Modifier),可以用来修饰各种需要被修饰的目标。我们可以对类、以及C#程序集中的成员进行进一步的描述。

简单地说,Attribute就是一种“附着物”——就像牡蛎吸附在船底或礁石上一样。 这些附着物的作用是为它们的附着体追加上一些额外的信息(这些信息保存在附着物的体内)——比如“这个类是我写的”或者“这个函数以前出过问题”等等

Attribute与注释的区别

注释是对程序源代码的一种说明,主要目的是给人看的,在程序被编译的时候会被编译器所丢弃,因此,它丝毫不会影响到程序的执行。

Attribute是程序代码的一部分,它不但不会被编译器丢弃,而且还会被编译器编译进程序集(Assembly)的元数据(Metadata)里。在程序运行的时候,随时可以从元数据(元数据:.NET中元数据是指程序集中的命名空间、类、方法、属性等信息,这些信息是可以通过Reflection读取出来的。)中提取提取出这些附加信息,并以之决策程序的运行。

规定特性(Attribute)

特性的目的是告诉编译器把程序结构的某组元数据嵌入程序集,它可以放置在几乎所有的声明中(但特定的属性可能限制在其上有效的声明类型)。其语法为:

  • 在结构前放置特性片段来运用特性
  • 特性片段被方括号包围,其中是特性名和特性的参数列表

例:

// 不含参数的特性
[Serializable]    
public class MyClass{...}

// 带有参数的特性
[MyAttribute("firt","second","finally")]    
public class MyClass {...}

单个结构可以运用多个特性,使用时可以把独立的特性片段互相叠在一起或使用分成单个特性片段,特性之间用逗号分隔

// 独立的特性片段
[Serializable]  
[MyAttribute("firt","second","finally")]    
public class MyClass {...}
 
// 逗号分隔
[MyAttribute("firt","second","finally"), Serializable]    
public class MyClass {...}

某些属性对于给定实体可以指定多次。例如,Conditional 就是一个可多次使用的属性:

[Conditional("DEBUG"), Conditional("TEST1")]
void TraceMethod()
{
    // ...
}

预定义特性(Attribute)

.Net 框架提供了三种预定义特性:

  • AttributeUsage
  • Conditional
  • Obsolete

Conditional

这个预定义特性标记了一个条件方法,其执行依赖于指定的预处理标识符。

它会引起方法调用的条件编译,取决于指定的值,比如 Debug 或 Trace。例如,当调试代码时显示变量的值。

//#define abc
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _02_特性_Attribute_
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Test1();
            Test2();
        }


        //Conditional

        [Conditional("abc")]
        public static void Test1()
        {
            Console.WriteLine("Test1");
        }
        public static void Test2()
        {
            Console.WriteLine("Test2");
        }
    }
    
}

Obsolete

这个预定义特性标记了不应被使用的程序实体。它可以让您通知编译器丢弃某个特定的目标元素。例如,当一个新方法被用在一个类中,但是您仍然想要保持类中的旧方法,您可以通过显示一个应该使用新方法,而不是旧方法的消息,来把它标记为 obsolete(过时的)。

规定该特性的语法如下:

[Obsolete(
   message
)]
[Obsolete(
   message,
   iserror
)]

其中:

  • 参数 message,是一个字符串,描述项目为什么过时以及该替代使用什么。
  • 参数 iserror,是一个布尔值。如果该值为 true,编译器应把该项目的使用当作一个错误。默认值是 false(编译器生成一个警告)。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _02_特性_Attribute_
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Test1();
            Test2();
        }


        //"这个方法已经被弃用了,建议使用Test2方法代替"
        [Obsolete("这个方法已经被弃用了,建议使用Test2方法代替",true)]
        public static void Test1()
        {
            Console.WriteLine("Test1");
        }
        public static void Test2()
        {
            Console.WriteLine("Test2");
        }
    }
    
}

AttributeUsage

预定义特性 AttributeUsage 描述了如何使用一个自定义特性类。它规定了特性可应用到的项目的类型。

规定该特性的语法如下:

[AttributeUsage(
   validon,
   AllowMultiple=allowmultiple,
   Inherited=inherited
)]

其中:

  • 参数 validon 规定特性可被放置的语言元素。它是枚举器 AttributeTargets 的值的组合。默认值是 AttributeTargets.All。
  • 参数 allowmultiple(可选的)为该特性的 AllowMultiple 属性(property)提供一个布尔值。如果为 true,则该特性是多用的。默认值是 false(单用的)。
  • 参数 inherited(可选的)为该特性的 Inherited 属性(property)提供一个布尔值。如果为 true,则该特性可被派生类继承。默认值是 false(不被继承)。

例如:

[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property, 
AllowMultiple = true)]

创建并使用自定义特性包含四个步骤:

  • 声明自定义特性
  • 构建自定义特性
  • 在目标程序元素上应用自定义特性
  • 通过反射访问特性

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _02_特性_Attribute_
{
    internal class Program
    {
        static void Main(string[] args)
        {
          //  Test1();
            Test2();
        }


        //"这个方法已经被弃用了,建议使用Test2方法代替"
        [Obsolete("这个方法已经被弃用了,建议使用Test2方法代替",true)]
        public static void Test1()
        {
            Console.WriteLine("Test1");
        }
        public static void Test2()
        {
            Console.WriteLine("Test2");
        }
    }


    [AttributeUsage(AttributeTargets.All)]
    public class SomeThing : Attribute {


        private string name;
        private string date;

        public string Name
        {

            get { return name; }
            set { name = value; }


        }
        public string Date
        {

            get { return date; }
            set { date = value; }
        }

        public SomeThing(string n,string d)
        {
            Name = n;
            Date = d;
        }
    }

    [SomeThing("吴亦凡","2025.6.10")]
    class Test1
    {

    }
    [SomeThing("罗志祥", "2025.6.10")]
    class Test2
    {

    }
    [SomeThing("罗志祥", "2025.6.10")]
    enum Sex
    {

    }


}

反射(Reflection)

反射指程序可以访问、检测和修改它本身状态或行为的一种能力。

程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。

可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。

优缺点

优点:

  1. 反射提高了程序的灵活性和扩展性。
  2. 降低耦合性,提高自适应能力。
  3. 它允许程序创建和控制任何类的对象,无需提前硬编码目标类。

缺点:

  1. 性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和拓展性要求很高的系统框架上,普通程序不建议使用。
  2. 使用反射会模糊程序内部逻辑;程序员希望在源代码中看到程序的逻辑,反射却绕过了源代码的技术,因而会带来维护的问题,反射代码比相应的直接代码更复杂。

反射(Reflection)的用途

反射(Reflection)有下列用途:

  • 它允许在运行时查看特性(attribute)信息。
  • 它允许审查集合中的各种类型,以及实例化这些类型。
  • 它允许延迟绑定的方法和属性(property)。
  • 它允许在运行时创建新类型,然后使用这些类型执行一些任务。

加载程序集

原来在此项目中要想使用其他的类库,需要先引用,再使用。有了反射之后,可以配合特性拿到某个类库中某个元素的类型,创建具体的类的实例。这样此项目不就不需要【明确引用】另外一个类库,只需要把类库放到当前项目中的bin/debug下即可。增强了项目的灵活性。

反射获取对象成员


通过反射访问另外一个类库中的对象

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace _04_通过反射访问另外一个类库中的对象
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {

            //加载程序集(类库)
            Assembly assembly= Assembly.Load("ClassLibrary1");

            //实例化
            object person=assembly.CreateInstance("ClassLibrary1.Person");


            //访问成员

             Type t  = assembly.GetType("ClassLibrary1.Person");
             MethodInfo methodInfo  =    t.GetMethod("SayHello");
             PropertyInfo propertyInfo =    t.GetProperty("Name");

            propertyInfo.SetValue(person, "吴亦凡");

            string name= propertyInfo.GetValue(person).ToString();
            Console.WriteLine(name);

            t.GetProperty("Id").SetValue(person, 100);

            Console.WriteLine(t.GetProperty("Id").GetValue(person).ToString());


           string res = t.GetMethod("SayHello").Invoke(person, new object[] { "世界本无路"} ) as string;

            Console.WriteLine(res);


        }
    }
}

加密解密(了解,防面试)

加密算法分类:对称加密,非对称加密,散列算法加密。
对称加密:使用相同的密钥对数据进行加密和解密。如:DES算法、AES算法
非对称加密:使用一对公钥和私钥来进行加密和解密。如:RSA算法
散列算法加密:任意长度的数据转换为固定长度的哈希值。如:MD5算法、SHA算法

公钥:主要用来把明文加密成密文,用到key。
私钥:主要用来把密文解密成明文,用到的key。

常用加密算法了解:
https://blog.csdn.net/qq_22120623/article/details/135082799

MD5和DES加密及解密封装参考:
https://www.cnblogs.com/healer007/p/5062189.html


AESHelper

using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace EncryptTool
{
    public static class AESHelper
    {
        private static readonly string keyString = ConfigurationManager.AppSettings["AESKEY"];
        // 它使用128、192或256位密钥
        private static readonly byte[] Key = Encoding.UTF8.GetBytes(keyString); // 16字节密钥
        private static readonly byte[] IV = Encoding.UTF8.GetBytes(keyString);  // 16字节初始化向量

        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="data">明文</param>
        /// <returns>密文</returns>
        public static string Encrypt(string data)
        {
            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = Key;
                aesAlg.IV = IV;

                ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                        {
                            swEncrypt.Write(data);
                        }
                        return Convert.ToBase64String(msEncrypt.ToArray());
                    }
                }
            }
        }

        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="data">密文</param>
        /// <returns>明文</returns>
        public static string Decrypt(string data)
        {
            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = Key;
                aesAlg.IV = IV;

                ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

                byte[] bytes = Convert.FromBase64String(data);

                using (MemoryStream msDecrypt = new MemoryStream(bytes))
                {
                    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                        {
                            return srDecrypt.ReadToEnd();
                        }
                    }
                }
            }
        }

    }
}

DESHelper

using System;
using System.Configuration;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace EncryptTool
{
    public static class DESHelper
    {
        private static readonly string keyString = ConfigurationManager.AppSettings["DESKEY"];
        // 它使用56位密钥
        private static readonly byte[] Key = Encoding.UTF8.GetBytes(keyString); // 8字节密钥
        private static readonly byte[] IV = Encoding.UTF8.GetBytes(keyString);  // 8字节初始化向量

        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="data">明文</param>
        /// <returns>密文</returns>
        public static string Encrypt(string data)
        {
            using (DESCryptoServiceProvider des = new DESCryptoServiceProvider())
            {
                MemoryStream ms = new MemoryStream();
                CryptoStream encStream = new CryptoStream(ms, des.CreateEncryptor(Key, IV), CryptoStreamMode.Write);
                byte[] toEncrypt = Encoding.UTF8.GetBytes(data);
                encStream.Write(toEncrypt, 0, toEncrypt.Length);
                encStream.FlushFinalBlock();
                return Convert.ToBase64String(ms.ToArray());
            }
        }

        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="data">密文</param>
        /// <returns>明文</returns>
        public static string Decrypt(string data)
        {
            using (DESCryptoServiceProvider des = new DESCryptoServiceProvider())
            {
                byte[] dataArray = Convert.FromBase64String(data);
                MemoryStream ms = new MemoryStream();
                CryptoStream decStream = new CryptoStream(ms, des.CreateDecryptor(Key, IV), CryptoStreamMode.Write);
                decStream.Write(dataArray, 0, dataArray.Length);
                decStream.FlushFinalBlock();
                return Encoding.UTF8.GetString(ms.ToArray());
            }
        }
    }
}

MD5Helper

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace EncryptTool
{
    public static class MD5Helper
    {
        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="data">明文</param>
        /// <returns>密文</returns>
        public static string Encrypt(string data)
        {
            // MD5它将任意长度的数据转换为128位的哈希值。
            using (var md5 = MD5.Create())
            {
                byte[] bytes = Encoding.ASCII.GetBytes(data);
                byte[] hashBytes = md5.ComputeHash(bytes);

                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < hashBytes.Length; i++)
                {
                    sb.Append(hashBytes[i].ToString("X2")); 
                }
                return sb.ToString();
            }
        }

    }
}

RSAHelper

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace EncryptTool
{
    public static class RSAHelper
    {
        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="data">明文</param>
        /// <returns>密文</returns>
        public static string Encrypt(string data)
        {
            using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
            {
                byte[] encrypted = rsa.Encrypt(Encoding.UTF8.GetBytes(data), false);
                return Convert.ToBase64String(encrypted);
            }
        }

        /// <summary>
        /// 解密
        /// </summary>
        /// <param name="data">密文</param>
        /// <returns>明文</returns>
        public static string Decrypt(string data)
        {
            using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
            {
                byte[] decrypted = rsa.Decrypt(Encoding.UTF8.GetBytes(data), false);
                return Encoding.UTF8.GetString(decrypted);
            }
        }
    }
}

SHAHelper

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace EncryptTool
{
    public static class SHAHelper
    {
        /// <summary>
        /// 加密
        /// </summary>
        /// <param name="data">明文</param>
        /// <returns>密文</returns>
        public static string Encrypt(string data)
        {
            byte[] plainBytes = Encoding.UTF8.GetBytes(data);
            // 包括SHA-1、SHA-256、SHA-384和SHA-512。相比于MD5,SHA系列算法更安全。
            using (SHA256 cryptoProvider = new SHA256CryptoServiceProvider())
            {
                byte[] hashBytes = cryptoProvider.ComputeHash(plainBytes);
                StringBuilder hashBuilder = new StringBuilder();
                foreach (byte b in hashBytes)
                {
                    hashBuilder.Append(b.ToString("x2"));
                }
                return hashBuilder.ToString();
            }
        }
    }
}

单例模式

什么是设计模式?

设计模式(Design pattern) 是解决软件开发某些特定问题而提出的一些解决方案也可以理解成解决问题的一些思路。通过设计模式可以帮助我们增强代码的可重用性、可扩充性、 可维护性、灵活性好。我们使用设计模式最终的目的是实现代码的 高内聚 和 低耦合。

什么是高内聚和低耦合?

举例一个现实生活中的例子,例如一个公司,一般都是各个部门各司其职,互不干涉。各个部门需要沟通时通过专门的负责人进行对接。在软件里面也是一样的 一个功能模块只是关注一个功能,一个模块最好只实现一个功能。这个是所谓的内聚,模块与模块之间、系统与系统之间的交互,是不可避免的, 但是我们要尽量减少由于交互引起的单个模块无法独立使用或者无法移植的情况发生, 尽可能多的单独提供接口用于对外操作, 这个就是所谓的低耦合

什么是单例模式?

从“单例”字面意思上理解为一个类只有一个实例,所以单例模式也就是保证一个类只有一个实例的一种实现方法(设计模式其实就是帮助我们解决实际开发过程中的方法, 该方法是为了降低对象之间的耦合度,然而解决方法有很多种,所以前人就总结了一些常用的解决方法为书籍,从而把这本书就称为设计模式)

下面给出单例模式的一个官方定义:确保一个类只有一个实例,并提供一个全局访问点。

为什么会有单例模式?它在什么情况下使用的?

从单例模式的定义中我们可以看出:单例模式的使用自然是当我们的系统中某个对象只需要一个实例的情况,例如:操作系统中只能有一个任务管理器,操作文件时,同一时间内只允许一个实例对其操作等,既然现实生活中有这样的应用场景,自然在软件设计领域必须有这样的解决方案了(因为软件设计也是现实生活中的抽象),所以也就有了单例模式了。

单例模式和静态类的区别?

  1. 首先单例模式会提供给你一个全局唯一的对象,静态类只是提供给你很多静态方法,这些方法不用创建对象,通过类就可以直接调用;
  2. 单例模式的灵活性更高,方法可以被override,因为静态类都是静态方法,所以不能被override;
  3. 如果是一个非常大的对象,单例模式可以懒加载,静态类就无法做到

那么什么时候时候应该用静态类,什么时候应该用单例模式呢?首先如果你只是想使用一些工具方法,那么最好用静态类,静态类比单例类更快,因为静态的绑定是在编译期进行的。***如果你要维护状态信息,或者访问资源时,应该选用单例模式。***还可以这样说,当你需要面向对象的能力时(比如继承、多态)时,选用单例类,当你仅仅是提供一些方法时选用静态类。


下面给出单例模式的一个官方定义:**确保一个类只有一个实例,并提供一个全局访问点。**为了帮助大家更好地理解单例模式,大家可以结合下面的类图来进行理解,以及后面也会剖析单例模式的实现思路:

[外链图片转存中…(img-vrGLzGOI-1749552452848)]

为什么会有单例模式?它在什么情况下使用的?

从单例模式的定义中我们可以看出:单例模式的使用自然是当我们的系统中某个对象只需要一个实例的情况,例如:操作系统中只能有一个任务管理器,操作文件时,同一时间内只允许一个实例对其操作等,既然现实生活中有这样的应用场景,自然在软件设计领域必须有这样的解决方案了(因为软件设计也是现实生活中的抽象),所以也就有了单例模式了。

单例模式和静态类的区别?

  1. 首先单例模式会提供给你一个全局唯一的对象,静态类只是提供给你很多静态方法,这些方法不用创建对象,通过类就可以直接调用;
  2. 单例模式的灵活性更高,方法可以被override,因为静态类都是静态方法,所以不能被override;
  3. 如果是一个非常大的对象,单例模式可以懒加载,静态类就无法做到

那么什么时候时候应该用静态类,什么时候应该用单例模式呢?首先如果你只是想使用一些工具方法,那么最好用静态类,静态类比单例类更快,因为静态的绑定是在编译期进行的。***如果你要维护状态信息,或者访问资源时,应该选用单例模式。***还可以这样说,当你需要面向对象的能力时(比如继承、多态)时,选用单例类,当你仅仅是提供一些方法时选用静态类。