【问题标题】:How can I create a class at runtime in c#? I want the class to be created physically in my file system如何在 C# 中在运行时创建一个类?我希望在我的文件系统中物理创建类
【发布时间】:2020-03-03 05:52:00
【问题描述】:

我正在开发一个扩展,它必须创建类,用户只提供类名和属性信息,以及我稍后将扩展的更多内容。

我还想添加必要的命名空间。

我看到很多问题都在解决这个问题。我尝试了使用反射.emit 和 CodeDom 的解决方案,但似乎没有一个可以在我的文件系统中物理创建类。有什么地方可以看或开始吗?

我的意思是物理类,cs文件应该出现在我的文件系统中。我说的是一个具有构造函数和一些属性、函数的泛型类。

【问题讨论】:

  • Reflection API 有助于在运行时将类型注入 Assembly。您似乎正在寻找的是代码生成器。也许你必须为自己选择一个。
  • 我怎么写一个?我应该使用 IL 吗?
  • 物理课是什么样的?你在谈论代码吗?类库?
  • 我的意思是物理类,cs文件应该出现在我的文件系统中。我说的是一个具有构造函数和一些属性、函数的泛型类。
  • 了解 T4 模板。如果您使用过 DB first 实体框架 - 请记住实体类是如何生成的。 docs.microsoft.com/en-us/visualstudio/modeling/…

标签: c# system.reflection reflection.emit codedom intermediate-language


【解决方案1】:

您可以使用 Microsoft CodeDom 来执行此操作。下面是一些示例代码。

使用此代码的最简单方法是在 Visual Studio 中创建一个新的“控制台应用程序 (.NET Core)”,然后覆盖 Program 类的内容(覆盖 Main 方法)。然后将鼠标悬停在曲线上,Visual Studio 灯泡会提示“安装包 System.CodeDom”。对最新版本执行此操作,并添加一个“使用 System.CodeDom.Compiler”,它应该可以编译。

如果您运行它,它会在 bin/Debug/netcoreapp3.1 文件夹中生成一个名为 MyClass.cs 的文件,其中包含用于具有属性和方法的类的 C# 代码。如您所见,名称和类型在下面的代码中设置。

Microsoft 对这些东西有很好的文档:there's a page,其中还展示了如何编译和运行以这种方式生成的代码。

using System;
using System.CodeDom;
using System.CodeDom.Compiler;

namespace ConsoleApp1
{
    class Program
    {
        static void Main()
        {
            string codeFileName = "MyClass.cs";
            CodeCompileUnit codeCompileUnit = CreateClass();
            CodeDomProvider codeDomProvider = CodeDomProvider.CreateProvider("CSharp");
            IndentedTextWriter tw = new IndentedTextWriter(new System.IO.StreamWriter(codeFileName, false), "    ");
            codeDomProvider.GenerateCodeFromCompileUnit(codeCompileUnit, tw, new CodeGeneratorOptions());
            tw.Close();
        }

        public static CodeCompileUnit CreateClass()
        {
            CodeCompileUnit compileUnit = new CodeCompileUnit();

            CodeNamespace samples = new CodeNamespace("MyNamespace");
            compileUnit.Namespaces.Add(samples);

            CodeTypeDeclaration class1 = new CodeTypeDeclaration("MyClass");
            samples.Types.Add(class1);

            CodeMemberField field1 = new CodeMemberField("System.String", "myField");
            class1.Members.Add(field1);

            CodeMemberProperty property1 = new CodeMemberProperty();
            property1.Name = "MyProperty";
            property1.Type = new CodeTypeReference("System.String");
            property1.Attributes = MemberAttributes.Public;
            property1.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "myField")));
            property1.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "myField"), new CodePropertySetValueReferenceExpression()));
            class1.Members.Add(property1);

            CodeMemberMethod method1 = new CodeMemberMethod();
            method1.Name = "MyMethod";
            method1.ReturnType = new CodeTypeReference("System.String");
            method1.Parameters.Add(new CodeParameterDeclarationExpression("System.String", "myParameter"));
            method1.Statements.Add(new CodeMethodReturnStatement(new CodeArgumentReferenceExpression("myParameter")));
            class1.Members.Add(method1);

            return compileUnit;
        }
    }
}

【讨论】:

  • 谢谢,当我将它作为控制台项目运行时,它确实创建了类。是否可以更改创建类的位置
  • 当然:只需更改 codeFileName 以包含路径。它可以是相对路径。 '字符串代码文件名 = @"c:/DotNet/MyClass.cs";'将文件放在 c:/DotNet 中。 '字符串代码文件名 = @"../../../MyClass.cs";'会将文件放在与 csproj 相同的文件夹中,比原始文件夹高三个级别。
  • 是的,谢谢,这也很有效。 CodeDom 可以用于向现有类添加属性吗?或向由 codedom 创建的类添加属性。
  • 我不确定你的意思。如果您想读取我们刚刚创建的代码文件,对其进行解析,并使用 CodeDom 对其进行修改,那么我认为您不能轻易做到:您需要一个解析器。如果您的意思是您可以在内存中动态更改类,那么 dynamic 关键字可以让您这样做。无论如何,我认为这是一个新问题,而不是评论!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-10-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多