反射和动态内存程序集

本文关键字:程序集 内存 动态 反射 | 更新日期: 2023-09-27 18:15:09

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

Assembly1:

public class DynamicTypeA { }

Assembly2:

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在后端使用类型调查,当调查的类型恰好存在于内存程序集中时,它们会失败。例如表达式。Bind将MemberInfo作为第一个参数,并使用它来验证第二个参数中提供的表达式的类型是否与成员的类型匹配。当此类型恰好位于内存程序集表达式中时。绑定失败。

有谁能想到解决办法吗?

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

谢谢马努

反射和动态内存程序集

结果是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();
    }

这段代码使用了您的代码片段,效果非常好。

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

        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 );

如果由于某种原因你在这里有问题,你肯定在做更复杂的事情。

我发现了问题:我需要将动态编译的程序集加载到当前的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);
    }
}