【发布时间】:2019-05-10 12:36:12
【问题描述】:
我正在使用以下课程:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
我有一个包含以下内容的字符串:
public class PersonActions
{
public static void Greet(Person p)
{
string test = p.Name;
}
}
在使用 WPF (.NET 4.7) 开发的客户端应用程序中,我在运行时编译此字符串并调用 Greet 方法,如下所示:
//Person x = new Person();
//x.Name = "Albert";
//x.Age = 76;
var assembly = Assembly.LoadFile(pathToAsseblyContainingPersonClass);
Type t = assembly.GetType("Person");
var x = Activator.CreateInstance(t);
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
parameters.ReferencedAssemblies.Add(pathToAsseblyContainingPersonClass);
//code being the code from abrom above (PersonActions)
CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
Assembly importassembly = results.CompiledAssembly;
Type assemblytype = importassembly.GetType("PersonActions");
ConstructorInfo constructor = assemblytype.GetConstructor(Type.EmptyTypes);
object classObject = constructor.Invoke(new object[] { });// not used for anything
MethodInfo main = assemblytype.GetMethod("Greet");
main.Invoke(classObject, new object[] { x });
不幸的是,这总是崩溃,因为即使类型来自同一个程序集,它也无法找到具有相同参数类型的方法。
抛出的错误是“System.IO.FileNotFoundException”,尽管这没有多大意义。不是找不到文件,是方法重载。
不知何故,它只是在寻找:
public static void Greet(object p)
仅使用“对象”作为参数类型是可行的,但在我的情况下是不可能的。
有没有办法接收对象的类型?或者可以告诉 Invocation 方法类型匹配?
编辑:
我猜我在上面的代码和测试中都犯了错误: 如前所述(现在已在上面评论)声明 Person 可以正常工作:
Person x = new Person();
x.Name = "Albert";
x.Age = 76;
使用 Activator.Createinstance(现在在上面更正)从组件中动态创建 Person x 不起作用。好像var x = Activator.CreateInstance(t);
导致 x 仍然是“对象”而不是“人”。
编辑 2: 这是该问题的最小工作示例:
有一个包含一个 WPF 应用程序的解决方案。 MainWindow.cs 包含:
using Microsoft.CSharp;
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Example
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
string code = @"public class PersonActions
{
public static void Greet(Person p)
{
}
}";
//Change to an absolute path if there is an exception
string pathToAsseblyContainingPersonClass = System.IO.Path.GetFullPath(@"..\..\..\Person\bin\Debug\Person.dll");
var assembly = Assembly.LoadFile(pathToAsseblyContainingPersonClass);
Type t = assembly.GetType("Person");
var x = Activator.CreateInstance(t);
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
parameters.ReferencedAssemblies.Add(pathToAsseblyContainingPersonClass);
//code being the code from abrom above (PersonActions)
CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
Assembly importassembly = results.CompiledAssembly;
Type assemblytype = importassembly.GetType("PersonActions");
ConstructorInfo constructor = assemblytype.GetConstructor(Type.EmptyTypes);
object classObject = constructor.Invoke(new object[] { });// not used for anything
MethodInfo main = assemblytype.GetMethod("Greet");
main.Invoke(classObject, new object[] { x });
}
}
}
并包含一个名为“Person”的类库项目包含:(注意没有命名空间)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
编辑 3:我最终得到了什么
感谢@Adam Benson,我可以确定整个问题。总体问题是,当前的应用程序域不允许直接从其他应用程序域加载加载程序集。就像亚当指出的那样,有三种解决方案(在链接的微软文章中)。第三个也是最容易实现的解决方案是使用 AssemblyResolve 事件。虽然这是一个很好的解决方案,但让我的应用程序遇到异常来解决这个问题让我心痛。
就像 Adam 还指出的那样,如果将 dll 直接放入 exe 所在的文件夹中,则会出现另一个异常。这只是部分正确,因为只有在比较原始 Debug 文件夹程序集中的 Person 和从 appdomain 程序集中加载的 Person 时才会出现邪恶双胞胎错误(基本上,如果您在两个目录中都有 dll)
仅从 exe 所在的文件夹加载程序集可以解决 FileNotFound 和 evil twin 错误:
旧:System.IO.Path.GetFullPath(@"..\..\..\Person\bin\Debug\Person.dll");
新:System.IO.Path.GetFullPath(@"Person.dll");
所以我最终做的是首先将必要的程序集复制到当前工作目录中:
File.Copy(pathToAsseblyContainingPersonClass, currentDir + @"\\Person.dll" , true);
【问题讨论】:
-
"...不幸的是,这总是崩溃..." - 您应该发布实际的异常消息。此外,在这种情况下,提及
WPF并不真正相关 -
还不清楚为什么需要 classObject,因为您正在调用静态方法。
Invoke的第一个参数被忽略。 -
你使用的GetMethod()重载只能找到实例方法。你需要 BindingFlags.Public |绑定标志。静态。并将 null 作为第一个参数传递给 Invoke(),因为它是静态的。
-
简单的解释是你忘记在你的问题中好好记录一下:这两个类存在于不同的程序集中。并且即时编译器找不到包含“Person”的那个,从而产生 FileNotFoundException。完全猜测,根本没有问题或代码 sn-p 的支持,但问题符合事故。使用 Fuslogvw.exe 解决程序集解析问题。
标签: c# .net reflection invoke