当以编程方式使用已编译的C#时,如何访问当前命名空间中的方法?
在回顾了所有关于以编程方式编译c#代码的答案和问题后,我选择了以下方法: 编译运行时当以编程方式使用已编译的C#时,如何访问当前命名空间中的方法?,c#,.net,wpf,code-generation,C#,.net,Wpf,Code Generation,在回顾了所有关于以编程方式编译c#代码的答案和问题后,我选择了以下方法: 编译运行时 using System; using System.CodeDom.Compiler; using System.Reflection; using Microsoft.CSharp; using AA.UI.WPF.WND; namespace AA.UI.WPF.COMMON { public static class CompileCSCAtRuntime { publ
using System;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;
using AA.UI.WPF.WND;
namespace AA.UI.WPF.COMMON
{
public static class CompileCSCAtRuntime
{
public static void HelloWorld()
{
string code = @"
using System;
using System.Windows;
using System.Windows.Forms;
using System.Reflection;
namespace AA.UI.WPF.COMMON
{
public class Program
{
public static void Main()
{
var aassembly = Assembly.LoadFrom(@""Path_To_Assembly"");
Type CompileCSCAtRuntime = aassembly.GetType(""AA.UI.WPF.COMMON.CompileCSCAtRuntime"");
Type Login = aassembly.GetType(""AA.UI.WPF.WND.Login"");
MethodInfo AccessLogin = CompileCSCAtRuntime.GetMethod(""AccessLogin"");
dynamic L = AccessLogin.Invoke(null, null);
L.ShowMessage(""hi"");
}
}
}
";
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
string[] refr = {
"System",
"System.Windows",
"System.Windows.Forms",
"System.Drawing",
"Microsoft.CSharp",
"System.Core",
"System.Data"};
foreach (string r in refr)
parameters.ReferencedAssemblies.Add($"{r}.dll");
parameters.ReferencedAssemblies.Add($"Path_To_Assembly");
parameters.GenerateInMemory = true;
parameters.GenerateExecutable = true;
CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
if (results.Errors.Count > 0)
{
MessageBox.Show(
results.Errors
.Cast<CompilerError>()
.Select(error => error.ErrorText)
.Aggregate((s, s1) => s += $";\r\n\r\n{s1}"));
return;
}
Assembly assembly = results.CompiledAssembly;
Type program = assembly.GetType("AA.UI.WPF.COMMON.Program");
MethodInfo main = program.GetMethod("Main");
main.Invoke(null, null);
}
public static Login AccessLogin()
{
return Login.Instance;
}
}
}
更新
using System;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;
using AA.UI.WPF.WND;
namespace AA.UI.WPF.COMMON
{
public static class CompileCSCAtRuntime
{
public static void HelloWorld()
{
string code = @"
using System;
using System.Windows;
using System.Windows.Forms;
using System.Reflection;
namespace AA.UI.WPF.COMMON
{
public class Program
{
public static void Main()
{
var aassembly = Assembly.LoadFrom(@""Path_To_Assembly"");
Type CompileCSCAtRuntime = aassembly.GetType(""AA.UI.WPF.COMMON.CompileCSCAtRuntime"");
Type Login = aassembly.GetType(""AA.UI.WPF.WND.Login"");
MethodInfo AccessLogin = CompileCSCAtRuntime.GetMethod(""AccessLogin"");
dynamic L = AccessLogin.Invoke(null, null);
L.ShowMessage(""hi"");
}
}
}
";
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
string[] refr = {
"System",
"System.Windows",
"System.Windows.Forms",
"System.Drawing",
"Microsoft.CSharp",
"System.Core",
"System.Data"};
foreach (string r in refr)
parameters.ReferencedAssemblies.Add($"{r}.dll");
parameters.ReferencedAssemblies.Add($"Path_To_Assembly");
parameters.GenerateInMemory = true;
parameters.GenerateExecutable = true;
CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
if (results.Errors.Count > 0)
{
MessageBox.Show(
results.Errors
.Cast<CompilerError>()
.Select(error => error.ErrorText)
.Aggregate((s, s1) => s += $";\r\n\r\n{s1}"));
return;
}
Assembly assembly = results.CompiledAssembly;
Type program = assembly.GetType("AA.UI.WPF.COMMON.Program");
MethodInfo main = program.GetMethod("Main");
main.Invoke(null, null);
}
public static Login AccessLogin()
{
return Login.Instance;
}
}
}
如果我不使用反射,它的工作很好
在编辑之前,我问我如何能够访问动态编译的c#code之外的方法,我对@BionicCode的答案感到满意。谢谢他。见评论
你的回答完全正确。既然你发布了第一个答案,我就告诉你这是真的。如你所说,我使用动态类型
但现在最后一件事是,我无法访问私有或内部方法,RuntimeBinderException:'AA.UI.WPF.WND.Login.ShowMessage(string)'由于其保护级别而无法访问。我觉得这很正常
目标
我的目标是以字符串的形式注入一些代码,并像其他普通代码一样运行这些代码而不进行反射,因为它太复杂,并且无法访问其他类、类型等。。。在当前命名空间FD.UI.WPF内。如果您知道另一种更简单的方法,请提供。就像您引用其他程序集一样,您必须创建并引用自己的程序集
- 创建另一个C#项目,
AssemblyOfMessageHelper
,其中包含一个新类,例如MessageHelper
,该类包含ShowMessage
- 然后在动态编译的代码中,调用
MessageHelper.ShowMessage
- 确保它具有使用NamespaceOfMessageHelper的
代码>和
- 您引用程序集
参数。ReferencedAssemblys.Add(“AssemblyOfMessageHelper.dll”)代码>
- 请记住,程序集需要位于可以找到它的文件夹中,因为
System.*
程序集位于GAC(.NET framework)或适当的SDK框架位置(.NET core),并且这两个位置都不会有AssemblyOfMessageHelper.dll
更新:正如其他人指出的,您可以通过引用自己的程序集来进一步简化此过程:参数。ReferencedAssemblys.Add(“MainProject.exe”)但是,我仍然认为创建一个单独的程序集是首选方法。像往常一样,了解您收到的错误消息将非常有趣。我想你得到了一个编译器错误
但一般来说,您应该能够执行来自其他程序集的方法
根据您的代码,您必须添加对编译器参数
的正确引用,这是包含您希望引用的编译器运行时
类型的程序集,例如,AA.UI.WPF.COMMON.dll
(此时我不知道真正的程序集名称):
现在您可以使用以下代码(注意使用导入添加的):
为了增加灵活性,您必须动态加载包含类型的程序集,然后使用MethodInfo
(反射)执行该方法。这样,您就不需要向编译后的代码添加任何程序集引用或使用
导入来添加任何程序集引用:
string code = @"
using System;
using System.Windows;
using System.Reflection;
namespace AA.UI.WPF.COMMON
{
public class Program
{
public static void Main()
{
var assembly = Assembly.LoadFrom(@""Path_To_Assembly"");
Type type = assembly.GetType(""AA.UI.WPF.COMMON.CompileCSCAtRuntime"");
MethodInfo method = type.GetMethod(""ShowMessage"");
method.Invoke(null, new[] { ""hello"" });
}
}
}";
您可以使用此代码段输出或记录编译器错误:
CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
if (results.Errors.Count > 0)
{
MessageBox.Show(
results.Errors
.Cast<CompilerError>()
.Select(error => error.ErrorText)
.Aggregate((result, currentValue) => result += $";{Environment.NewLine}{currentValue}"));
}
使用实例时,您将绑定到已编译的访问规则,即Login.ShowMessage
是internal
(您知道internal
限制对程序集范围的访问)。
由于动态编译的代码被编译成新的动态程序集,因此调用方的作用域(动态代码)不再满足此访问约束
要绕过此可见性约束,必须使用反射访问和调用非公共成员。要获取任何非公共成员的MemberInfo
,您必须始终指定适当的BindingFlags
组合:
string code = @"
using System;
using System.Windows;
using System.Reflection;
namespace AA.UI.WPF.COMMON
{
public class Program
{
public static void Main()
{
var assembly = Assembly.LoadFrom(@""Path_To_Assembly"");
Type compileCSCAtRuntimeType = assembly.GetType(""AA.UI.WPF.COMMON.CompileCSCAtRuntime"");
MethodInfo accessLoginMethod = compileCSCAtRuntimeType.GetMethod(""AccessLogin"");
object resultInstance = accessLoginMethod.Invoke(null, null);
Type resultType = resultInstance.GetType();
MethodInfo resultMethod = resultType.GetMethod(""ShowMessage"",
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
resultMethod.Invoke(resultInstance, new [] { ""hi"" });
}
}
}";
我的项目的输出是一个.exe文件,而不是.dll。所以它不起作用。另一方面,我希望访问AA.UI.WPF命名空间中的所有方法。如果可以,请编写一个快速代码段。现有项目的输出为.exe,但您创建了另一个DLL项目。是否有方法访问.exe项目中的现有方法?而不是创建一个新的.dll文件@taThere先生不需要创建额外的组件。您可以添加当前程序集。@KrisVandermotten是的,对。我已经添加了.exe文件,它工作了;您提供的最后一个代码帮助了我,问题是我希望与当前命名空间内的其他内部代码一样使用外部代码。目前,我可以访问ShowMessage()方法,但目标是在ShowMessage()是私有或内部方法时访问它。我想远程向我的应用程序中注入一些代码,所以我应该访问我的其他方法。你的建议是什么?首先我意识到我的代码中有一些错误。引用的类型应该是CompileCSCAtRuntime
,而不是Program
:assembly.GetType(“AA.UI.WPF.COMMON.CompileCSCAtRuntime”)代码>。您还需要导入反射命名空间:使用System.Reflection代码>。我不知道我是否理解你的意思。因为您使用的是反射,所以您可以访问任何类型,无论是公共的、静态的还是私有的。你只需要知道你想要哪种类型。您可以使用Assembly.GetTypes
枚举程序集中声明的所有类型。我添加了一个版本,该版本通过在编译的代码中使用反射来修复您的初始问题。如果您希望加载在运行时添加到文件夹中的程序集,您可以使用.NET的一部分。它使用起来很简单。我不知道这是否是您的意图(动态加载程序集并执行某些类型的方法。第一个解决方案是添加对编译器参数的引用,例如参数..referencedAssemblys.add(“AA.UI.WPF.WND.dll”);
(基本上是包含类型的程序集的程序集名称-可以是.exe或.dll程序集)
CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
if (results.Errors.Count > 0)
{
MessageBox.Show(
results.Errors
.Cast<CompilerError>()
.Select(error => error.ErrorText)
.Aggregate((result, currentValue) => result += $";{Environment.NewLine}{currentValue}"));
}
dynamic L = AccessLogin.Invoke(null, null);
L.ShowMessage(""hi"");
string code = @"
using System;
using System.Windows;
using System.Reflection;
namespace AA.UI.WPF.COMMON
{
public class Program
{
public static void Main()
{
var assembly = Assembly.LoadFrom(@""Path_To_Assembly"");
Type compileCSCAtRuntimeType = assembly.GetType(""AA.UI.WPF.COMMON.CompileCSCAtRuntime"");
MethodInfo accessLoginMethod = compileCSCAtRuntimeType.GetMethod(""AccessLogin"");
object resultInstance = accessLoginMethod.Invoke(null, null);
Type resultType = resultInstance.GetType();
MethodInfo resultMethod = resultType.GetMethod(""ShowMessage"",
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
resultMethod.Invoke(resultInstance, new [] { ""hi"" });
}
}
}";