是否可以在 .NET 代码中找到泛型方法调用的所有具体类型?
Is it possible to find all concrete types of generic method calls in .NET code?
我正在编写一个 C# 代码生成器来序列化对象,以便通过网络发送它们。
起点是这个(简体):
public static partial class Serialization
{
public static void Serialize<T>(in T value, DataStream stream)
{
throw new NotImplementedException($"Don't know how to serialize type {typeof(T)}!");
}
public static void Deserialize<T>(out T target, DataStream stream)
{
throw new NotImplementedException($"Don't know how to deserialize type {typeof(T)}!");
}
}
现在,序列化代码生成器将为所有需要序列化的类型生成额外的非泛型 Serialize
和 Deserialize
方法,例如结构 Vector3
和 public float
字段 x
、y
和 z
:
public static partial class Serialization
{
// automatically generated method
public static void Serialize(in Vector3 value, DataStream stream)
{
stream.Write(value.x);
stream.Write(value.y);
stream.Write(value.z);
}
// automatically generated method
public static void Deserialize(out Vector3 target, DataStream stream)
{
target.x = stream.ReadFloat();
target.y = stream.ReadFloat();
target.z = stream.ReadFloat();
}
}
开头显示的泛型方法仅用于在尚未(尚未)为某个类型生成序列化代码的情况下防止编译器错误。我需要编译代码,否则我无法对其使用反射。
目前我必须使用自定义 EnableSerialization
属性标记需要序列化代码的类型。
理想情况下,生成器会查看编译后的代码(使用静态代码分析),识别可能传递给通用 Serialize
和 Deserialize
方法的类型,然后生成代码那些类型。因此,例如,如果我的代码中某处有这个:
int x = 42;
Serialization.Serialize(x, new DataStream());
然后代码生成器应该选择 int
作为需要序列化代码的类型。
是否有这种努力的任何秘诀,或者 .NET 库或第三方库中是否有任何东西可以促进这一点?
(我考虑过 运行 时间代码生成,但我更愿意将其作为预处理步骤。)
使用 Mono.Reflection
,您可以执行以下操作:
HashSet<Type> types = new HashSet<Type>();
Assembly assembly = Assembly.LoadFile(@"<Path>");
foreach (Module module in assembly.GetModules())
{
foreach (Type type in module.GetTypes())
{
// GetMethodBody() should not be null since otherwise Disassembler.GetInstructions would throw an exception
foreach (MethodInfo method in type.GetMethods().Where(m => m.GetMethodBody() != null))
{
foreach (Instruction instruction in Disassembler.GetInstructions(method))
{
// instruction.Operand being MethodInfo most probably means a call instrution.
// Not sure if this always true
MethodInfo called = instruction.Operand as MethodInfo;
if (called != null && called.DeclaringType.Name.Equals("Serialization") && called.Name.Equals("Serialize"))
{
types.Add(called.GetParameters()[0].ParameterType);
}
}
}
}
}
Disassembler
和 Instruction
都是 Mono.Reflection
的一部分。
现在,您已将所有使用的类型传递给 types
.
中的 Serialization.Serialize
我正在编写一个 C# 代码生成器来序列化对象,以便通过网络发送它们。
起点是这个(简体):
public static partial class Serialization
{
public static void Serialize<T>(in T value, DataStream stream)
{
throw new NotImplementedException($"Don't know how to serialize type {typeof(T)}!");
}
public static void Deserialize<T>(out T target, DataStream stream)
{
throw new NotImplementedException($"Don't know how to deserialize type {typeof(T)}!");
}
}
现在,序列化代码生成器将为所有需要序列化的类型生成额外的非泛型 Serialize
和 Deserialize
方法,例如结构 Vector3
和 public float
字段 x
、y
和 z
:
public static partial class Serialization
{
// automatically generated method
public static void Serialize(in Vector3 value, DataStream stream)
{
stream.Write(value.x);
stream.Write(value.y);
stream.Write(value.z);
}
// automatically generated method
public static void Deserialize(out Vector3 target, DataStream stream)
{
target.x = stream.ReadFloat();
target.y = stream.ReadFloat();
target.z = stream.ReadFloat();
}
}
开头显示的泛型方法仅用于在尚未(尚未)为某个类型生成序列化代码的情况下防止编译器错误。我需要编译代码,否则我无法对其使用反射。
目前我必须使用自定义 EnableSerialization
属性标记需要序列化代码的类型。
理想情况下,生成器会查看编译后的代码(使用静态代码分析),识别可能传递给通用 Serialize
和 Deserialize
方法的类型,然后生成代码那些类型。因此,例如,如果我的代码中某处有这个:
int x = 42;
Serialization.Serialize(x, new DataStream());
然后代码生成器应该选择 int
作为需要序列化代码的类型。
是否有这种努力的任何秘诀,或者 .NET 库或第三方库中是否有任何东西可以促进这一点?
(我考虑过 运行 时间代码生成,但我更愿意将其作为预处理步骤。)
使用 Mono.Reflection
,您可以执行以下操作:
HashSet<Type> types = new HashSet<Type>();
Assembly assembly = Assembly.LoadFile(@"<Path>");
foreach (Module module in assembly.GetModules())
{
foreach (Type type in module.GetTypes())
{
// GetMethodBody() should not be null since otherwise Disassembler.GetInstructions would throw an exception
foreach (MethodInfo method in type.GetMethods().Where(m => m.GetMethodBody() != null))
{
foreach (Instruction instruction in Disassembler.GetInstructions(method))
{
// instruction.Operand being MethodInfo most probably means a call instrution.
// Not sure if this always true
MethodInfo called = instruction.Operand as MethodInfo;
if (called != null && called.DeclaringType.Name.Equals("Serialization") && called.Name.Equals("Serialize"))
{
types.Add(called.GetParameters()[0].ParameterType);
}
}
}
}
}
Disassembler
和 Instruction
都是 Mono.Reflection
的一部分。
现在,您已将所有使用的类型传递给 types
.
Serialization.Serialize