基于字符串中定义的数据类型在运行时生成结构

时间:2011-02-14 12:09:59

标签: c# .net

如何在运行时从字符串中定义的一系列类型生成结构?

我有一个标题,例如包含“float,float,byte [255]”。 我有跟在这个标题后面的二进制数据,并按顺序保存数据。 在运行时读取头文件之前我不知道数据类型,我想生成一个结构,我可以用来将二进制数据编组成一个结构数组。

有什么想法吗?

2 个答案:

答案 0 :(得分:3)

显然你知道你的应用程序的要求是什么,但正如lejon所说,问题是如何在创建它时访问该结构。在c#中,你必须跳过箍,而不是你不需要显式声明类型的vb。

我可以想到几种方法来实现你想要的。您可以使用CodeDom生成代码,请参阅此http://www.15seconds.com/issue/020917.htm

就个人简单的结构而言,我会按顺序构建代码

  string szCode =@"using System;
  using System.Windows.Forms;

  namespace RunTimeCompile
  {
      [StructLayoutAttribute(LayoutKind.Sequential)]
      public string MyStruct
      {"

然后,对于头文件中的每个数据类型,将成员附加到szCode字符串。 (你需要构建一个基本函数来解析你的类型):

szCode += "float Field1;\n"; 
szCode += "float Field2;\n"; 
szCode += "byte[255] Field3;\n";

并关闭您的代码......

szeCode+=";\n;\n";

现在你有源代码使用CodeDom来编译它。

oCodeDomProvider = CodeDomProvider.CreateProvider("CSharp");
// Add what referenced assemblies
CompilerParameters oCompilerParameters = new CompilerParameters();
oCompilerParameters.ReferencedAssemblies.Add("system.dll");
// set the compiler to create a DLL
oCompilerParameters.GenerateExecutable = false;
// set the dll to be created in memory and not on the hard drive
oCompilerParameters.GenerateInMemory = true;
oCompilerResults =
  oCodeDomProvider.CompileAssemblyFromSource(oCompilerParameters, szCode);

注意:您也可以使用CompileAssemblyFromFile从文件而不是内存源代码进行编译

现在在继续检查之前没有编译错误

if (oCompilerResults.Errors.Count!=0) return; // add you own error handling

现在您可以像这样

检索动态生成的结构的实例
oAssembly = oCompilerResults.CompiledAssembly;
oObject = oAssembly.CreateInstance("RunTimeCompile.MyStruct");
oType = oObject.GetType();

现在,您可以读取二进制文件的所有行,并使用Marshal或您希望序列化二进制数据的任何其他方法(可能是二进制序列化程序)将它们编组为MyStruct []数组。

例如使用Runtime.Interop.Marshal你可以做这样的事情(你需要稍微调整一下,特别是你不能在你的代码中声明MyStruct [],所以你需要做一些像oObjectArray = oAssembly.CreateInstance(“RunTimeCompile.MyStruct []”)声明destValues结果数组):

byte[] sourceData = ReadSourceData(); // TODO: generate method to load your BLOB
MyStruct[] destValues = new MyStruct[Marshal.SizeOf(oType) + 1]
int arrayIndex = 0;

GCHandle handle = GCHandle.Alloc(sourceData, GCHandleType.Pinned);
try
{
IntPtr buffer = handle.AddrOfPinnedObject();
buffer = (IntPtr)(buffer.ToInt32() +
(arrayIndex*Marshal.SizeOf(typeof(MyStruct))));
destStruct = (MyStruct)Marshal.PtrToStructure(buffer, typeof(MyStruct));
}
finally
{
handle.Free();
}

 return MyStruct;

现在你有了你的数组结构。

答案 1 :(得分:0)

问题是,一旦你定义了你的结构,你将如何访问它?您无法静态引用它,因为该类型仅适用于运行时。您可能必须使用反射,委托或dynamic。有没有更好的方法来做你想要的事情(比如,一个单独的类接受字节数组并根据需要解释它)而不是在运行时生成结构?