反射和动态内存中的程序集

时间:2014-07-27 09:44:04

标签: c# reflection

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

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

有人能想到解决方案吗?

动态创建类型并写入它们会影响运行环境,这很糟糕,但没有反映这些类型毫无价值。

由于 马努

2 个答案:

答案 0 :(得分: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();

    }

这会使用您的代码段,就像魅力一样。

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

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

如果出于某种原因你在这里遇到问题,你肯定会做一些更复杂的事情。

答案 1 :(得分:0)

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