当以编程方式使用已编译的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

在回顾了所有关于以编程方式编译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
    {
        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"" });
            }
          }
        }";