Hi,前段日子写了 一个动态生成对象实体类的CodeDom
然后这两天在使用中发现,诶~怎么就只能是使用基本类型,我 想要一些复杂类型不可以吗,比如我自定义的类或者和我本地的类型进行交互。
终于,最后
各种摸索之下,解决问题了
先看下原版的内容
public object CreateInstance(string context, string fullNamespaceClass) => CreateInstance(() => { #region Verify if (string.IsNullOrEmpty(context)) { throw new ArgumentException("生成的代码不能为空"); } if (string.IsNullOrEmpty(fullNamespaceClass)) { throw new ArgumentException("命名空间和类名称不能为空"); } #endregion #region 加载构建 var refPaths = new[] { typeof(System.Object).GetTypeInfo().Assembly.Location, typeof(Console).GetTypeInfo().Assembly.Location, Path.Combine(Path.GetDirectoryName(typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly.Location), "System.Runtime.dll") }; MetadataReference[] references = refPaths.Select(r => MetadataReference.CreateFromFile(r)).ToArray(); CSharpCompilation compilation = CSharpCompilation.Create( Path.GetRandomFileName(), syntaxTrees: new[] { CSharpSyntaxTree.ParseText(context) }, references: references, options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); #endregion #region 创建对象 using (var ms = new MemoryStream()) { EmitResult result = compilation.Emit(ms); if (result.Success) { ms.Seek(0, SeekOrigin.Begin); Assembly assembly = AssemblyLoadContext.Default.LoadFromStream(ms); //var type = assembly.GetType("CodeDOM.CodeDOMCreatedClass"); return assembly.CreateInstance(fullNamespaceClass); } else { return result.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error); } } #endregion });
说实话,在一开始我不知道要怎么做,网上面对于这方面的教程也还是很少。
但是如果你理解反射和IL的运行相信不难猜出它怎么工作的,。
于是乎,我就摸瓜一样的把问题摸到了这里
var refPaths = new[] { typeof(System.Object).GetTypeInfo().Assembly.Location, typeof(Console).GetTypeInfo().Assembly.Location, Path.Combine(Path.GetDirectoryName(typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly.Location), "System.Runtime.dll") }; MetadataReference[] references = refPaths.Select(r => MetadataReference.CreateFromFile(r)).ToArray();
经过实践得知,或者说直接一看也能看明白,这里就是加载程序集的地方。
那么既然可以加载系统的,为什么不可以加载我自己的呢?
说干就干
List<string> dllFullPath = new List<string>(); DirectoryInfo root1 = new DirectoryInfo(Directory.GetCurrentDirectory()); foreach (FileInfo f in root1.GetFiles()) { if (f.Name.Contains(".dll", StringComparison.OrdinalIgnoreCase)) { dllFullPath.Add(f.FullName); } } dllFullPath.Add(typeof(System.Object).GetTypeInfo().Assembly.Location); dllFullPath.Add(typeof(Console).GetTypeInfo().Assembly.Location); dllFullPath.Add(Path.Combine(Path.GetDirectoryName(typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly.Location), "System.Runtime.dll")); MetadataReference[] references = dllFullPath.ToArray().Select(r => MetadataReference.CreateFromFile(r)).ToArray();
很清晰了吧。
就这样,就解决问题了。
但是这样做不太好,毕竟每次的IO还是不小,所以,一次过后可以把地址都缓存起来,这样就好多拉~
加载过后,在使用中不要忘记,要AddNameSpace呦~
就写这里吧,把问题说清楚就可以了,