【问题标题】:Reflection and dynamic in-memory assemblies反射和动态内存程序集
【发布时间】:2014-09-18 17:24:47
【问题描述】:

假设我使用 CSharpCodeProvider 动态创建了一个类型,并选择不保留结果。生成的程序集仅存在于内存中。 假设我在两个不同的内存程序集中创建了两种类型:

组装1:

public class DynamicTypeA { }

组装2:

public class DynamicTypeB
{
       public DynamicTypeA MyProperty { get; set; }
}

如您所见,第二种类型具有第一种类型的属性。 凉爽的。现在我想用反射来探索 DynamicTypeB:

foreach (PropertyInfo pi in typeof(DynamicTypeB).GetProperties())
{
    Console.WriteLine(pi.PropertyType.Name);
}

事实证明,当程序集不在磁盘上时,PropertyInfo.PropertyType 失败!!! 对于 MemberInfo 和所有其他类型调查构造都是如此。

众所周知,许多 .Net API 在后端使用类型调查,当调查的类型恰好存在于内存程序集中时,它们会失败。例如 Expression.Bind 将 MemberInfo 作为第一个参数,并使用它来验证第二个参数中提供的表达式的类型是否与成员的类型匹配。当此类型恰好位于内存程序集中时,Expression.Bind 失败。

谁能想到解决办法?

动态创建类型并将它们写入磁盘会污染运行环境,这很糟糕,但如果没有反射,这些类型毫无价值。

谢谢 手动

【问题讨论】:

    标签: c# reflection


    【解决方案1】:

    事实证明,当程序集不在磁盘上时,PropertyInfo.PropertyType 失败

    你确定吗?看看:

        static void Main( string[] args )
        {
            string code = @"
                 namespace foo {
                     public class DynamicTypeA { }
                     public class DynamicTypeB {
                         public DynamicTypeA MyProperty { get; set; } 
                     }
                 }       
            ";
    
            CSharpCodeProvider csp = new CSharpCodeProvider();
    
            CompilerParameters p = new CompilerParameters();
            p.GenerateInMemory = true;
    
            var results = csp.CompileAssemblyFromSource( p, code );
    
            foreach ( Type type in results.CompiledAssembly.GetTypes() )
            {
                Console.WriteLine( type.Name );
    
                foreach ( PropertyInfo pi in type.GetProperties() )
                {
                    Console.WriteLine( "\t{0}", pi.PropertyType.Name );
                }
            }
    
            Console.ReadLine();
    
        }
    

    这会使用您的 sn-p 并像魅力一样工作。

    将循环移动到动态代码内部并没有太大变化,它仍然有效:

            string code = @"
                 using System;
                 using System.Reflection; 
                 namespace foo {
                     public class DynamicTypeA { }
                     public class DynamicTypeB {
                         public DynamicTypeA MyProperty { get; set; } 
                     }
    
                     public class DynamicTypeC {
                         public void Foo() {
                            foreach ( PropertyInfo pi in typeof(DynamicTypeB).GetProperties() )
                            {
                                Console.WriteLine( pi.PropertyType.Name );
                            }
                         } 
                     }
                 }       
            ";
    
            CSharpCodeProvider csp = new CSharpCodeProvider();
    
            CompilerParameters p = new CompilerParameters();
            p.GenerateInMemory = true;
    
            var results = csp.CompileAssemblyFromSource( p, code );
    
            var c = results.CompiledAssembly.CreateInstance( "foo.DynamicTypeC" );
            var typeC = c.GetType();
    
            typeC.InvokeMember( "Foo", BindingFlags.InvokeMethod | 
                BindingFlags.Public | BindingFlags.Instance, null, c, null );
    

    如果由于某种原因您在这里遇到问题,那么您肯定在做一些更复杂的事情。

    【讨论】:

      【解决方案2】:

      我发现了问题: 我需要将动态编译的程序集加载到当前的 AppDomain 中,然后才能通过反射检索任何信息。

      我要感谢 Sam Alavi 向我解释了这一点。 这是带有必要修复的代码:

      public class HomeController : Controller
      {
          public Assembly AssemblyA { get; set; }
          public Assembly AssemblyB { get; set; }
      
          public ActionResult Index()
          {
              var provider = new CSharpCodeProvider();
              var parametersA = new CompilerParameters();
              parametersA.GenerateInMemory = true;
              parametersA.OutputAssembly = "dynamicA.dll";
      
              var code1 = @"namespace DynamicA {  public class DynamicClassA  {  } }";
              var result1 = provider.CompileAssemblyFromSource(parametersA, code1);
              this.AssemblyA = result1.CompiledAssembly;
      
              var parametersB = new CompilerParameters();
              parametersA.GenerateInMemory = true;
              parametersB.ReferencedAssemblies.Add("dynamicA.dll");
              parametersB.OutputAssembly = "dynamicB.dll";
              var code2 = @"using DynamicA; namespace DynamicB { public class DynamicB { public DynamicClassA MyProperty { get; set; } } }";
      
              var results2 = provider.CompileAssemblyFromSource(parametersB, code2);
              this.AssemblyB = results2.CompiledAssembly;
      
              AppDomain.CurrentDomain.AssemblyResolve += (sender, e) =>
              {
                  if (e.Name.Contains("dynamicA"))
                      return this.AssemblyA;
                  if (e.Name.Contains("dynamicB"))
                      return this.AssemblyB;
                  return null;
      
              };
              AppDomain.CurrentDomain.Load(this.AssemblyA.FullName);
              AppDomain.CurrentDomain.Load(this.AssemblyB.FullName);
      
      
              var t = results2.CompiledAssembly.DefinedTypes.First();
              var pi = t.GetProperty("MyProperty");
      
               var res = pi.PropertyType.Name;
      
              return View(res);
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-18
        • 1970-01-01
        • 1970-01-01
        • 2011-04-08
        • 1970-01-01
        相关资源
        最近更新 更多