using System.ComponentModel; using System.Reflection; using Common.Utils; namespace Common.Test; /// /// ReflectionHelper 反射工具测试 /// 展示类型扫描、属性访问、特性读取等功能 /// [TestClass] public class ReflectionHelperTests { #region 测试类定义 /// /// 测试基类 /// public abstract class BaseEntity { public int Id { get; set; } } /// /// 测试接口 /// public interface ITestService { void Execute(); } /// /// 测试特性 /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property)] public class TestAttribute : Attribute { public string Name { get; set; } public TestAttribute(string name) { Name = name; } } /// /// 测试实体类 /// [Test("UserEntity")] public class User : BaseEntity { [Test("UserName")] [System.ComponentModel.Description("用户名")] public string Name { get; set; } = ""; [System.ComponentModel.Description("用户年龄")] public int Age { get; set; } public string Email { get; set; } = ""; public DateTime CreatedAt { get; set; } = DateTime.Now; public void SayHello() { Console.WriteLine($"Hello, I'm {Name}"); } public static string GetVersion() { return "1.0.0"; } public int Calculate(int a, int b) { return a + b; } } /// /// 测试 DTO /// public class UserDto { public string Name { get; set; } = ""; public int Age { get; set; } public string Email { get; set; } = ""; } /// /// 测试服务实现 /// public class TestService : ITestService { public void Execute() { Console.WriteLine("Executing test service"); } } #endregion #region 类型扫描测试 [TestMethod] public void FindSubclassesOf_FindsAllSubclasses() { // Act var subclasses = ReflectionHelper.FindSubclassesOf(); // Assert Assert.IsTrue(subclasses.Count > 0); Assert.IsTrue(subclasses.Any(t => t == typeof(User))); } [TestMethod] public void FindImplementationsOf_FindsAllImplementations() { // Arrange var assembly = Assembly.GetExecutingAssembly(); // Act var implementations = ReflectionHelper.FindImplementationsOf(assembly); // Assert Assert.IsTrue(implementations.Count > 0); Assert.IsTrue(implementations.Any(t => t == typeof(TestService))); } [TestMethod] public void FindTypesWithAttribute_FindsTypesWithAttribute() { // Arrange var assembly = Assembly.GetExecutingAssembly(); // Act var types = ReflectionHelper.FindTypesWithAttribute(assembly); // Assert Assert.IsTrue(types.Count > 0); Assert.IsTrue(types.Any(t => t == typeof(User))); } #endregion #region 属性访问测试 [TestMethod] public void GetPropertyValue_ReturnsCorrectValue() { // Arrange var user = new User { Name = "Alice", Age = 25 }; // Act var name = ReflectionHelper.GetPropertyValue(user, nameof(User.Name)); var age = ReflectionHelper.GetPropertyValue(user, nameof(User.Age)); // Assert Assert.AreEqual("Alice", name); Assert.AreEqual(25, age); } [TestMethod] public void SetPropertyValue_SetsValueCorrectly() { // Arrange var user = new User(); // Act ReflectionHelper.SetPropertyValue(user, nameof(User.Name), "Bob"); ReflectionHelper.SetPropertyValue(user, nameof(User.Age), 30); // Assert Assert.AreEqual("Bob", user.Name); Assert.AreEqual(30, user.Age); } [TestMethod] public void GetPropertyValue_Performance_IsFasterThanDirectReflection() { // Arrange var user = new User { Name = "Alice", Age = 25 }; const int iterations = 10000; // 预热缓存 ReflectionHelper.GetPropertyValue(user, nameof(User.Name)); // Act: 使用 ReflectionHelper(有缓存) var sw1 = System.Diagnostics.Stopwatch.StartNew(); for (var i = 0; i < iterations; i++) _ = ReflectionHelper.GetPropertyValue(user, nameof(User.Name)); sw1.Stop(); // Act: 使用原生反射(无缓存) var propertyInfo = typeof(User).GetProperty(nameof(User.Name))!; var sw2 = System.Diagnostics.Stopwatch.StartNew(); for (var i = 0; i < iterations; i++) _ = propertyInfo.GetValue(user); sw2.Stop(); // Assert: ReflectionHelper 应该比直接反射快(由于缓存和表达式树) Console.WriteLine($"ReflectionHelper: {sw1.ElapsedMilliseconds}ms"); Console.WriteLine($"Direct Reflection: {sw2.ElapsedMilliseconds}ms"); // 注意:首次运行可能因为 JIT 编译而不准确,多次运行后 ReflectionHelper 应该更快 } [TestMethod] public void GetPropertyNames_ReturnsAllPropertyNames() { // Act var propertyNames = ReflectionHelper.GetPropertyNames(); // Assert Assert.IsTrue(propertyNames.Count >= 5); CollectionAssert.Contains(propertyNames, nameof(User.Name)); CollectionAssert.Contains(propertyNames, nameof(User.Age)); CollectionAssert.Contains(propertyNames, nameof(User.Email)); } [TestMethod] public void GetProperties_ReturnsAllProperties() { // Act var properties = ReflectionHelper.GetProperties(); // Assert Assert.IsTrue(properties.Length >= 5); Assert.IsTrue(properties.Any(p => p.Name == nameof(User.Name))); } #endregion #region 特性读取测试 [TestMethod] public void GetAttribute_ReturnsAttribute() { // Act var attribute = ReflectionHelper.GetAttribute(typeof(User)); // Assert Assert.IsNotNull(attribute); Assert.AreEqual("UserEntity", attribute.Name); } [TestMethod] public void GetAttribute_FromProperty_ReturnsAttribute() { // Arrange var property = typeof(User).GetProperty(nameof(User.Name))!; // Act var testAttr = ReflectionHelper.GetAttribute(property); var descAttr = ReflectionHelper.GetAttribute(property); // Assert Assert.IsNotNull(testAttr); Assert.AreEqual("UserName", testAttr.Name); Assert.IsNotNull(descAttr); Assert.AreEqual("用户名", descAttr.Description); } [TestMethod] public void HasAttribute_ChecksAttributeExistence() { // Act & Assert Assert.IsTrue(ReflectionHelper.HasAttribute(typeof(User))); Assert.IsFalse(ReflectionHelper.HasAttribute(typeof(UserDto))); } [TestMethod] public void GetAttribute_UsesCaching() { // Arrange var type = typeof(User); // Act: 多次获取应该使用缓存 var attr1 = ReflectionHelper.GetAttribute(type); var attr2 = ReflectionHelper.GetAttribute(type); // Assert: 应该返回相同的实例(来自缓存) Assert.IsNotNull(attr1); Assert.IsNotNull(attr2); } #endregion #region 创建实例测试 [TestMethod] public void CreateInstance_Generic_CreatesInstance() { // Act var user = ReflectionHelper.CreateInstance(); // Assert Assert.IsNotNull(user); Assert.IsInstanceOfType(user, typeof(User)); } [TestMethod] public void CreateInstance_ByType_CreatesInstance() { // Act var user = ReflectionHelper.CreateInstance(typeof(User)); // Assert Assert.IsNotNull(user); Assert.IsInstanceOfType(user, typeof(User)); } [TestMethod] public void TryCreateInstance_SuccessfulCreation_ReturnsTrue() { // Act var success = ReflectionHelper.TryCreateInstance(out var user); // Assert Assert.IsTrue(success); Assert.IsNotNull(user); } #endregion #region 属性复制测试 [TestMethod] public void CopyProperties_CopiesSameNameProperties() { // Arrange var source = new User { Name = "Alice", Age = 25, Email = "alice@example.com" }; var target = new UserDto(); // Act ReflectionHelper.CopyProperties(source, target); // Assert Assert.AreEqual("Alice", target.Name); Assert.AreEqual(25, target.Age); Assert.AreEqual("alice@example.com", target.Email); } [TestMethod] public void MapTo_CreatesNewInstanceAndCopiesProperties() { // Arrange var source = new User { Name = "Bob", Age = 30, Email = "bob@example.com" }; // Act var dto = ReflectionHelper.MapTo(source); // Assert Assert.IsNotNull(dto); Assert.AreEqual("Bob", dto.Name); Assert.AreEqual(30, dto.Age); Assert.AreEqual("bob@example.com", dto.Email); } [TestMethod] public void CopyProperties_IgnoreCase_CopiesCorrectly() { // Arrange var source = new { name = "Alice", AGE = 25 }; var target = new UserDto(); // Act ReflectionHelper.CopyProperties(source, target, true); // Assert Assert.AreEqual("Alice", target.Name); Assert.AreEqual(25, target.Age); } #endregion #region 方法调用测试 [TestMethod] public void InvokeMethod_CallsInstanceMethod() { // Arrange var user = new User { Name = "Alice" }; // Act var result = ReflectionHelper.InvokeMethod(user, nameof(User.Calculate), 10, 20); // Assert Assert.IsNotNull(result); Assert.AreEqual(30, result); } [TestMethod] public void InvokeStaticMethod_CallsStaticMethod() { // Act var result = ReflectionHelper.InvokeStaticMethod(typeof(User), nameof(User.GetVersion)); // Assert Assert.IsNotNull(result); Assert.AreEqual("1.0.0", result); } [TestMethod] [ExpectedException(typeof(InvalidOperationException))] public void InvokeMethod_NonExistentMethod_ThrowsException() { // Arrange var user = new User(); // Act ReflectionHelper.InvokeMethod(user, "NonExistentMethod"); } #endregion #region 类型判断测试 [TestMethod] public void IsNullableType_DetectsNullableTypes() { // Act & Assert Assert.IsTrue(ReflectionHelper.IsNullableType(typeof(int?))); Assert.IsFalse(ReflectionHelper.IsNullableType(typeof(int))); Assert.IsFalse(ReflectionHelper.IsNullableType(typeof(string))); } [TestMethod] public void GetUnderlyingType_ReturnsCorrectType() { // Act var underlyingType = ReflectionHelper.GetUnderlyingType(typeof(int?)); // Assert Assert.AreEqual(typeof(int), underlyingType); } [TestMethod] public void IsValueType_DistinguishesValueTypes() { // Act & Assert Assert.IsTrue(ReflectionHelper.IsValueType(typeof(int))); Assert.IsTrue(ReflectionHelper.IsValueType(typeof(DateTime))); Assert.IsFalse(ReflectionHelper.IsValueType(typeof(string))); Assert.IsFalse(ReflectionHelper.IsValueType(typeof(User))); } [TestMethod] public void IsNumericType_DetectsNumericTypes() { // Act & Assert Assert.IsTrue(ReflectionHelper.IsNumericType(typeof(int))); Assert.IsTrue(ReflectionHelper.IsNumericType(typeof(double))); Assert.IsTrue(ReflectionHelper.IsNumericType(typeof(decimal))); Assert.IsTrue(ReflectionHelper.IsNumericType(typeof(int?))); Assert.IsFalse(ReflectionHelper.IsNumericType(typeof(string))); Assert.IsFalse(ReflectionHelper.IsNumericType(typeof(DateTime))); } [TestMethod] public void IsCollectionType_DetectsCollections() { // Act & Assert Assert.IsTrue(ReflectionHelper.IsCollectionType(typeof(int[]))); Assert.IsTrue(ReflectionHelper.IsCollectionType(typeof(List))); Assert.IsTrue(ReflectionHelper.IsCollectionType(typeof(IEnumerable))); Assert.IsFalse(ReflectionHelper.IsCollectionType(typeof(int))); Assert.IsFalse(ReflectionHelper.IsCollectionType(typeof(string))); } #endregion #region 泛型操作测试 [TestMethod] public void IsGenericType_DetectsGenericTypes() { // Act & Assert Assert.IsTrue(ReflectionHelper.IsGenericType(typeof(List))); Assert.IsTrue(ReflectionHelper.IsGenericType(typeof(Dictionary))); Assert.IsFalse(ReflectionHelper.IsGenericType(typeof(int))); Assert.IsFalse(ReflectionHelper.IsGenericType(typeof(string))); } [TestMethod] public void GetGenericArguments_ReturnsTypeArguments() { // Act var args = ReflectionHelper.GetGenericArguments(typeof(Dictionary)); // Assert Assert.AreEqual(2, args.Length); Assert.AreEqual(typeof(string), args[0]); Assert.AreEqual(typeof(int), args[1]); } [TestMethod] public void MakeGenericType_ConstructsGenericType() { // Act var listType = ReflectionHelper.MakeGenericType(typeof(List<>), typeof(string)); // Assert Assert.AreEqual(typeof(List), listType); } #endregion #region 缓存管理测试 [TestMethod] public void ClearCache_ClearsAllCaches() { // Arrange: 先访问一些缓存 ReflectionHelper.GetPropertyValue(new User { Name = "Test" }, nameof(User.Name)); ReflectionHelper.GetAttribute(typeof(User)); // Act ReflectionHelper.ClearCache(); // Assert: 缓存应该被清空(无法直接验证,但不应抛异常) Assert.IsTrue(true); } [TestMethod] public void ClearPropertyAccessorCache_ClearsPropertyCache() { // Arrange ReflectionHelper.GetPropertyValue(new User { Name = "Test" }, nameof(User.Name)); // Act ReflectionHelper.ClearPropertyAccessorCache(); // Assert Assert.IsTrue(true); } #endregion #region 实际应用场景测试 [TestMethod] public void Scenario_PluginScanning() { // Arrange: 模拟插件系统,扫描所有实现 ITestService 的类 var assembly = Assembly.GetExecutingAssembly(); // Act: 查找所有插件 var plugins = ReflectionHelper.FindImplementationsOf(assembly); // Assert Assert.IsTrue(plugins.Count > 0); // Act: 实例化并执行插件 foreach (var pluginType in plugins) if (ReflectionHelper.CreateInstance(pluginType) is ITestService plugin) plugin.Execute(); // 输出:Executing test service } [TestMethod] public void Scenario_AutoMapper() { // Arrange: 模拟 DTO 映射 var user = new User { Id = 1, Name = "Alice", Age = 25, Email = "alice@example.com", CreatedAt = DateTime.Now }; // Act: 自动映射到 DTO var dto = ReflectionHelper.MapTo(user); // Assert Assert.IsNotNull(dto); Assert.AreEqual("Alice", dto.Name); Assert.AreEqual(25, dto.Age); Assert.AreEqual("alice@example.com", dto.Email); } [TestMethod] public void Scenario_DynamicPropertyAccess() { // Arrange: 模拟动态表单数据 var user = new User { Name = "Bob", Age = 30 }; var propertyUpdates = new Dictionary { { nameof(User.Name), "Updated Bob" }, { nameof(User.Age), 31 }, { nameof(User.Email), "bob@updated.com" } }; // Act: 动态更新属性 foreach (var (propertyName, value) in propertyUpdates) ReflectionHelper.SetPropertyValue(user, propertyName, value); // Assert Assert.AreEqual("Updated Bob", user.Name); Assert.AreEqual(31, user.Age); Assert.AreEqual("bob@updated.com", user.Email); } [TestMethod] public void Scenario_AttributeBasedValidation() { // Arrange: 读取属性上的描述特性用于验证消息 var properties = typeof(User).GetProperties(); var validationMessages = new Dictionary(); // Act: 收集所有带描述的属性 foreach (var property in properties) { var description = ReflectionHelper.GetAttribute(property); if (description != null) validationMessages[property.Name] = $"{description.Description}不能为空"; } // Assert Assert.IsTrue(validationMessages.ContainsKey(nameof(User.Name))); Assert.AreEqual("用户名不能为空", validationMessages[nameof(User.Name)]); Assert.IsTrue(validationMessages.ContainsKey(nameof(User.Age))); Assert.AreEqual("用户年龄不能为空", validationMessages[nameof(User.Age)]); } [TestMethod] public void Scenario_TypeInspection() { // Arrange: 检查类型信息 var type = typeof(User); // Act: 收集类型信息 var info = new { IsClass = type.IsClass, IsAbstract = type.IsAbstract, BaseType = type.BaseType?.Name, HasTestAttribute = ReflectionHelper.HasAttribute(type), PropertyCount = ReflectionHelper.GetProperties().Length, IsValueType = ReflectionHelper.IsValueType(type) }; // Assert Assert.IsTrue(info.IsClass); Assert.IsFalse(info.IsAbstract); Assert.AreEqual("BaseEntity", info.BaseType); Assert.IsTrue(info.HasTestAttribute); Assert.IsTrue(info.PropertyCount >= 5); Assert.IsFalse(info.IsValueType); } #endregion }