反射
System.Reflection,.NET框架提供的帮助类库
好处:动态的
缺点:编写复杂,避开编译器检查,性能问题
GetType()
和 typeof()
都是在编程中用于获取类型信息的常用方法或操作符,但它们之间有一些重要的区别。这里我将以C#为例来说明它们之间的区别和作用,因为这两个概念在C#中非常常见。
1. GetType()
GetType()
是一个实例方法,它属于 System.Object
类,这意味着所有在C#中创建的类都继承了此方法。因此,你可以在任何对象实例上调用 GetType()
来获取该对象的运行时类型。
作用:
- 获取对象实例的运行时类型。
- 通常用于反射(Reflection)或类型检查。
示例:
csharp
string str = "Hello, World!";
Type type = str.GetType(); // type 现在是 String 类型
2. typeof()
typeof()
是一个C#操作符,用于在编译时获取类型信息。它接受一个类型名称(而非实例)作为参数,并返回表示该类型的 System.Type
对象。
作用:
- 获取类型的编译时信息。
- 常用于泛型编程、条件编译和类型检查。
示例:
csharp
Type type = typeof(string); // type 现在是 String 类型
区别
- 调用方式 :
GetType()
是一个实例方法,需要在对象实例上调用;而typeof()
是一个操作符,用于获取类型的静态信息。 - 执行时间 :
GetType()
在运行时执行,因此它可以获取对象实例的实际类型(即使该实例是某个类型的子类实例);而typeof()
在编译时执行,因此它只能获取到你在代码中明确指定的类型。 - 用途 :由于上述的执行时间差异,
GetType()
通常用于反射和动态类型检查;而typeof()
则常用于泛型编程和编译时类型检查。
实际上,GetType()
方法是在反射中常用的,因为它允许你在运行时获取对象的实际类型,而不仅仅是编译时声明的类型。而 typeof
关键字则通常用于在编译时获取类型信息,它并不直接用于反射,但在与反射相关的操作中可能间接用到。
下面是一个使用 GetType()
方法进行反射的示例:
csharp
using System;
using System.Reflection;
class MyClass
{
public void MyMethod()
{
Console.WriteLine("MyClass.MyMethod called");
}
}
class Program
{
static void Main()
{
MyClass myObj = new MyClass();
// 使用GetType()方法获取对象的类型
Type type = myObj.GetType();
// 使用反射调用MyMethod方法
MethodInfo methodInfo = type.GetMethod("MyMethod");
if (methodInfo != null)
{
methodInfo.Invoke(myObj, null); // 调用无参的MyMethod方法
}
}
}
在这个示例中,我们创建了一个 MyClass
的实例 myObj
,并使用 GetType()
方法获取了它的类型。然后,我们使用 Type
对象的 GetMethod()
方法来获取 MyMethod
方法的 MethodInfo
对象。最后,我们使用 MethodInfo
对象的 Invoke()
方法来调用该方法。
而 typeof
关键字在反射中可能间接使用,比如当你需要获取一个类型的 Type
对象但又没有该类型的实例时:
csharp
Type type = typeof(MyClass); // 获取MyClass的Type对象,而不需要MyClass的实例
在这个例子中,我们使用 typeof(MyClass)
来直接获取 MyClass
类型的 Type
对象,而不需要创建 MyClass
的实例。这在静态类型检查和泛型编程中特别有用。但是,请注意,这并没有直接参与反射操作,而是为反射提供了类型信息。
作用
1.动态类型信息
csharp
using System;
using System.Reflection;
public class MyClass
{
public int MyProperty { get; set; }
public void MyMethod() { }
}
class Program
{
static void Main()
{
Type type = typeof(MyClass);
Console.WriteLine("Type Name: " + type.Name); // 输出 "MyClass"
Console.WriteLine("Base Type: " + type.BaseType.Name); // 输出 "Object"
// 获取所有公共属性
PropertyInfo[] properties = type.GetProperties();
foreach (var property in properties)
{
Console.WriteLine("Property: " + property.Name);
}
// 获取所有公共方法
MethodInfo[] methods = type.GetMethods();
foreach (var method in methods)
{
Console.WriteLine("Method: " + method.Name);
}
}
}
2. 动态对象实例化
csharp
using System;
using System.Reflection;
public class MyClass
{
public string Message { get; set; }
}
class Program
{
static void Main()
{
Type type = typeof(MyClass);
object instance = Activator.CreateInstance(type); // 创建 MyClass 的实例
// 假设 MyClass 有一个公共的无参构造函数
// 如果需要带参数的构造函数,请使用带参数的 Activator.CreateInstance 方法
// 通过反射设置属性值
PropertyInfo property = type.GetProperty("Message");
property.SetValue(instance, "Hello, Reflection!");
// 通过反射获取属性值
string message = (string)property.GetValue(instance);
Console.WriteLine(message); // 输出 "Hello, Reflection!"
}
}
3. 动态方法调用
csharp
using System;
using System.Reflection;
public class MyClass
{
public int Add(int a, int b)
{
return a + b;
}
}
class Program
{
static void Main()
{
Type type = typeof(MyClass);
object instance = Activator.CreateInstance(type); // 创建 MyClass 的实例
// 获取 Add 方法的 MethodInfo
MethodInfo method = type.GetMethod("Add");
// 调用 Add 方法并传入参数
object result = method.Invoke(instance, new object[] { 1, 2 });
// 输出结果
Console.WriteLine((int)result); // 输出 3
}
}
4. 动态属性访问(已在 2 中包含)
5. 动态程序集加载
csharp
using System;
using System.Reflection;
class Program
{
static void Main()
{
// 加载程序集(这里假设你有一个名为 "MyAssembly.dll" 的程序集)
Assembly assembly = Assembly.LoadFrom("MyAssembly.dll");
// 获取程序集中的类型
Type type = assembly.GetType("MyAssembly.MyClass");
// 后续可以创建实例、调用方法等...
}
}
请注意,由于动态加载程序集通常涉及文件系统上的文件,因此上述示例中的 "MyAssembly.dll"
需要替换为实际的程序集文件名和路径。
6. 自定义序列化
csharp
using System;
using System.IO;
using System.Reflection;
public class MyClass
{
public int Id { get; set; }
public string Name { get; set; }
}
class Program
{
static void Main()
{
MyClass obj = new MyClass { Id = 1, Name = "Test" };
// 自定义序列化方法,使用反射获取属性值
string serialized = SerializeObject(obj);
Console.WriteLine(serialized); // 输出类似 "{Id: 1, Name: Test}"
// 假设有一个反序列化方法(这里省略实现细节)
MyClass deserialized = DeserializeObject<MyClass>(serialized);
Console.WriteLine($"Id: {deserialized.Id}, Name: {deserialized.Name}"); // 输出 "Id: 1, Name: Test"
}
static string SerializeObject(object obj)
{
Type type = obj.GetType();
var properties = type.GetProperties();
using (var sw = new StringWriter())
{
sw.Write("{");
bool first = true;
foreach (var property in properties)
{
if (!first) sw.Write(", ");
first = false;
sw.Write($"{property.Name}: {property.GetValue(obj, null)}");
}
sw.Write("}");
return sw.ToString();
}
}
static T DeserializeObject<T>(string serialized)
{
// 这里省略了反序列化的实现细节,因为这会涉及到解析字符串和设置对象属性的逻辑
throw new NotImplementedException();
}
}
7. 框架和库开发(示例可能很抽象)
框架和库开发的示例通常涉及更复杂的逻辑,但这里可以用一个简单的依赖注入容器示例来展示反射的使用:
csharp
using System;
using System.Reflection;
public interface IService
{
void DoWork();
}
public class MyService : IService
{
public void DoWork()
{
Console.WriteLine("Service is working.");
}
}
public class ServiceContainer
{
private readonly Type _serviceType;
public ServiceContainer(Type serviceType)
{
_serviceType = serviceType;
}
public IService Resolve()
{
return (IService)Activator.CreateInstance(_serviceType);
}
}
class Program
{
static void Main()
{
ServiceContainer container = new ServiceContainer(typeof(MyService));
IService service = container.Resolve();
service.DoWork(); // 输出 "Service is working."
}
}
8. 单元测试(使用 NUnit 框架作为示例)
csharp
using NUnit.Framework;
using System.Reflection;
public class MyClass
{
// ... 类的其他成员 ...
private int PrivateMethod()
{
return 42; // 假设这是一个私有方法
}
}
[TestFixture]
public class MyClassTests
{
[Test]
public void TestPrivateMethod()
{
MyClass obj = new MyClass();
// 使用反射调用私有方法
MethodInfo method = typeof(MyClass).GetMethod("PrivateMethod", BindingFlags.NonPublic | BindingFlags.Instance);
int result = (int)method.Invoke(obj, null);
Assert.AreEqual(42, result);
}
}
9. 反编译器工具(这通常是一个独立的应用,不直接写代码示例)
反编译器工具(如 ILSpy、dotPeek 等)使用反射的概念来读取和解析 .NET 程序集(DLL 或 EXE 文件),但它们并不是通过 C# 代码中的 System.Reflection
命名空间来实现的。这些工具通常使用更底层的 API 或自定义的解析器来读取和理解 .NET 元数据。
10.依赖注入容器(DI Container)的简化示例,它展示了如何注册服务、解析服务和使用构造函数注入。
csharp
using System;
using System.Collections.Concurrent;
public interface IService
{
void DoWork();
}
public class MyService : IService
{
public void DoWork()
{
Console.WriteLine("MyService is working.");
}
}
public class DependencyContainer
{
private readonly ConcurrentDictionary<Type, object> _services = new ConcurrentDictionary<Type, object>();
public void Register<TService, TImplementation>()
where TService : class
where TImplementation : TService, new()
{
_services.TryAdd(typeof(TService), new TImplementation());
}
public TService Resolve<TService>()
where TService : class
{
if (_services.TryGetValue(typeof(TService), out object service))
{
return (TService)service;
}
throw new InvalidOperationException($"No service registered for type {typeof(TService).FullName}");
}
}
public class SomeClassThatDependsOnService
{
private readonly IService _service;
public SomeClassThatDependsOnService(IService service)
{
_service = service;
}
public void DoSomething()
{
_service.DoWork();
Console.WriteLine("SomeClassThatDependsOnService is doing something.");
}
}
class Program
{
static void Main()
{
var container = new DependencyContainer();
// 注册服务
container.Register<IService, MyService>();
// 解析服务
IService service = container.Resolve<IService>();
service.DoWork(); // 输出 "MyService is working."
// 使用构造函数注入
SomeClassThatDependsOnService dependentClass = container.Resolve<SomeClassThatDependsOnService>();
dependentClass.DoSomething(); // 输出 "MyService is working." 和 "SomeClassThatDependsOnService is doing something."
}
}
在这个例子中,DependencyContainer
类有两个主要方法:Register
用于注册服务,Resolve
用于解析服务。Register
方法将实现类型 TImplementation
注册为服务类型 TService
的实例。Resolve
方法则根据请求的服务类型返回相应的服务实例。
SomeClassThatDependsOnService
类展示了如何使用构造函数注入来获取依赖的服务实例。在 Main
方法中,我们首先注册服务,然后解析服务以展示其基本用法,最后通过构造函数注入来展示如何在实际应用中使用依赖注入。