如何使用codedom的CodeMemberProperty为属性getter或setter生成DebuggerStep

本文关键字:getter setter 生成 DebuggerStep 属性 何使用 codedom CodeMemberProperty | 更新日期: 2023-09-27 18:09:51

我如何生成一个DebuggerStepThroughAttribute在getter/setter与CodeDOM?

这个问题来自MSDN文档和StackOverflow上的一个问题。

如何使用codedom的CodeMemberProperty为属性getter或setter生成DebuggerStep

CodeMemberProperty的CustomAttributes类型是CodeAttributeDeclarationCollection。如果在这里指定了Attribute,那么它将被添加到属性声明行之上:生成的代码将无法编译。

CodeMemberProperty的GetStatements和SetStatements是集合:我不能在它们上面指定自定义属性。

这是我在反射器的帮助下在微软CSharpCodeGenerator中看到的:

private void GenerateProperty(CodeMemberProperty e, CodeTypeDeclaration c)
{
    if ((this.IsCurrentClass || this.IsCurrentStruct) || this.IsCurrentInterface)
    {
        if (e.CustomAttributes.Count > 0)
        {
            this.GenerateAttributes(e.CustomAttributes);
        }
        if (!this.IsCurrentInterface)
        {
            if (e.PrivateImplementationType == null)
            {
                this.OutputMemberAccessModifier(e.Attributes);
                this.OutputVTableModifier(e.Attributes);
                this.OutputMemberScopeModifier(e.Attributes);
            }
        }
        else
        {
            this.OutputVTableModifier(e.Attributes);
        }
        this.OutputType(e.Type);
        this.Output.Write(" ");
        if ((e.PrivateImplementationType != null) && !this.IsCurrentInterface)
        {
            this.Output.Write(this.GetBaseTypeOutput(e.PrivateImplementationType));
            this.Output.Write(".");
        }
        if ((e.Parameters.Count > 0) && (string.Compare(e.Name, "Item", StringComparison.OrdinalIgnoreCase) == 0))
        {
            this.Output.Write("this[");
            this.OutputParameters(e.Parameters);
            this.Output.Write("]");
        }
        else
        {
            this.OutputIdentifier(e.Name);
        }
        this.OutputStartingBrace();
        this.Indent++;
        if (e.HasGet)
        {
            if (this.IsCurrentInterface || ((e.Attributes & MemberAttributes.ScopeMask) == MemberAttributes.Abstract))
            {
                this.Output.WriteLine("get;");
            }
            else
            {
                this.Output.Write("get");
                this.OutputStartingBrace();
                this.Indent++;
                this.GenerateStatements(e.GetStatements);
                this.Indent--;
                this.Output.WriteLine("}");
            }
        }
        if (e.HasSet)
        {
            if (this.IsCurrentInterface || ((e.Attributes & MemberAttributes.ScopeMask) == MemberAttributes.Abstract))
            {
                this.Output.WriteLine("set;");
            }
            else
            {
                this.Output.Write("set");
                this.OutputStartingBrace();
                this.Indent++;
                this.GenerateStatements(e.SetStatements);
                this.Indent--;
                this.Output.WriteLine("}");
            }
        }
        this.Indent--;
        this.Output.WriteLine("}");
    }
}

仔细检查if (e.HasGet)周围的线条,似乎不可能。

我认为你做不到。Microsoft已经放弃了CodeDom命名空间,转而支持T4代码生成技术。

已经有好几年没有添加任何新内容了。我很确定最后一次添加是在。net 2.0中。在那之后,什么都不是。

所以,如果你正在创建任何新的生成代码,移动到T4

下面是一个演示如何做到这一点的LinqPad代码片段。这是一个主要的hack,因为正如GregC的回答所示,使用结构化Dom类是不可能的。它基本上生成属性,然后编辑字符串以插入属性。

void Main()
{
    Sample sample = new Sample();
            sample.AddFields();
            sample.AddProperties();
            sample.AddMethod();
            sample.AddConstructor();
            sample.AddEntryPoint();
            sample.GenerateCSharpCode();
}
public class Sample{
        /// <summary> 
        /// Define the compile unit to use for code generation.  
        /// </summary>
        CodeCompileUnit targetUnit;
        /// <summary> 
        /// The only class in the compile unit. This class contains 2 fields, 
        /// 3 properties, a constructor, an entry point, and 1 simple method.  
        /// </summary>
        CodeTypeDeclaration targetClass;
        /// <summary> 
        /// The name of the file to contain the source code. 
        /// </summary> 
        private const string outputFileName = "SampleCode.cs";
        /// <summary> 
        /// Define the class. 
        /// </summary> 
        public Sample()
        {
            targetUnit = new CodeCompileUnit();
            CodeNamespace samples = new CodeNamespace("CodeDOMSample");
            samples.Imports.Add(new CodeNamespaceImport("System"));
            targetClass = new CodeTypeDeclaration("CodeDOMCreatedClass");
            targetClass.IsClass = true;
            targetClass.TypeAttributes =
                TypeAttributes.Public | TypeAttributes.Sealed;
            samples.Types.Add(targetClass);
            targetUnit.Namespaces.Add(samples);
        }
        /// <summary> 
        /// Adds two fields to the class. 
        /// </summary> 
        public void AddFields()
        {
            // Declare the widthValue field.
            CodeMemberField widthValueField = new CodeMemberField();
            widthValueField.Attributes = MemberAttributes.Private;
            widthValueField.Name = "widthValue";
            widthValueField.Type = new CodeTypeReference(typeof(System.Double));
            widthValueField.Comments.Add(new CodeCommentStatement(
                "The width of the object."));
            targetClass.Members.Add(widthValueField);
            // Declare the heightValue field
            CodeMemberField heightValueField = new CodeMemberField();
            heightValueField.Attributes = MemberAttributes.Private;
            heightValueField.Name = "heightValue";
            heightValueField.Type =
                new CodeTypeReference(typeof(System.Double));
            heightValueField.Comments.Add(new CodeCommentStatement(
                "The height of the object."));
            targetClass.Members.Add(heightValueField);
        }
        /// <summary> 
        /// Add three properties to the class. 
        /// </summary> 
        public void AddProperties()
        {
            // Declare the read-only Width property.
            CodeMemberProperty widthProperty = new CodeMemberProperty();
            widthProperty.Attributes =
                MemberAttributes.Public | MemberAttributes.Final;
            widthProperty.Name = "Width";
            widthProperty.HasGet = true;
            widthProperty.HasSet = true;
            widthProperty.Type = new CodeTypeReference(typeof(System.Double));
            widthProperty.Comments.Add(new CodeCommentStatement(
                "The Width property for the object."));
            widthProperty.GetStatements.Add(new CodeMethodReturnStatement(
                new CodeFieldReferenceExpression(
                new CodeThisReferenceExpression(), "widthValue")));
            widthProperty.SetStatements.Add(new CodeMethodReturnStatement(
                new CodeFieldReferenceExpression(
                new CodeThisReferenceExpression(), "widthValue")));
            targetClass.Members.Add(widthProperty);
            // Declare the read-only Height property.
            CodeMemberProperty heightProperty = new CodeMemberProperty();
            heightProperty.Attributes =
                MemberAttributes.Public | MemberAttributes.Final;
            heightProperty.Name = "Height";
            heightProperty.HasGet = true;
            heightProperty.Type = new CodeTypeReference(typeof(System.Double));
            heightProperty.Comments.Add(new CodeCommentStatement(
                "The Height property for the object."));
            heightProperty.GetStatements.Add(new CodeMethodReturnStatement(
                new CodeFieldReferenceExpression(
                new CodeThisReferenceExpression(), "heightValue")));
            targetClass.Members.Add(heightProperty);
            // Declare the read only Area property.
            CodeMemberProperty areaProperty = new CodeMemberProperty();
            areaProperty.Attributes =
                MemberAttributes.Public | MemberAttributes.Final;
            areaProperty.Name = "Area";
            areaProperty.HasGet = true;
            areaProperty.Type = new CodeTypeReference(typeof(System.Double));
            areaProperty.Comments.Add(new CodeCommentStatement(
                "The Area property for the object."));
            // Create an expression to calculate the area for the get accessor  
            // of the Area property.
            CodeBinaryOperatorExpression areaExpression =
                new CodeBinaryOperatorExpression(
                new CodeFieldReferenceExpression(
                new CodeThisReferenceExpression(), "widthValue"),
                CodeBinaryOperatorType.Multiply,
                new CodeFieldReferenceExpression(
                new CodeThisReferenceExpression(), "heightValue"));
            areaProperty.GetStatements.Add(
                new CodeMethodReturnStatement(areaExpression));
            targetClass.Members.Add(areaProperty);
        }
        /// <summary> 
        /// Adds a method to the class. This method multiplies values stored  
        /// in both fields. 
        /// </summary> 
        public void AddMethod()
        {
            // Declaring a ToString method
            CodeMemberMethod toStringMethod = new CodeMemberMethod();
            toStringMethod.Attributes =
                MemberAttributes.Public | MemberAttributes.Override;
            toStringMethod.Name = "ToString";
            toStringMethod.ReturnType =
                new CodeTypeReference(typeof(System.String));
            CodeFieldReferenceExpression widthReference =
                new CodeFieldReferenceExpression(
                new CodeThisReferenceExpression(), "Width");
            CodeFieldReferenceExpression heightReference =
                new CodeFieldReferenceExpression(
                new CodeThisReferenceExpression(), "Height");
            CodeFieldReferenceExpression areaReference =
                new CodeFieldReferenceExpression(
                new CodeThisReferenceExpression(), "Area");
            // Declaring a return statement for method ToString.
            CodeMethodReturnStatement returnStatement =
                new CodeMethodReturnStatement();
            // This statement returns a string representation of the width, 
            // height, and area. 
            string formattedOutput = "The object:" + Environment.NewLine +
                " width = {0}," + Environment.NewLine +
                " height = {1}," + Environment.NewLine +
                " area = {2}";
            returnStatement.Expression =
                new CodeMethodInvokeExpression(
                new CodeTypeReferenceExpression("System.String"), "Format",
                new CodePrimitiveExpression(formattedOutput),
                widthReference, heightReference, areaReference);
            toStringMethod.Statements.Add(returnStatement);
            targetClass.Members.Add(toStringMethod);
        }
        /// <summary> 
        /// Add a constructor to the class. 
        /// </summary> 
        public void AddConstructor()
        {
            // Declare the constructor
            CodeConstructor constructor = new CodeConstructor();
            constructor.Attributes =
                MemberAttributes.Public | MemberAttributes.Final;
            // Add parameters.
            constructor.Parameters.Add(new CodeParameterDeclarationExpression(
                typeof(System.Double), "width"));
            constructor.Parameters.Add(new CodeParameterDeclarationExpression(
                typeof(System.Double), "height"));
            // Add field initialization logic
            CodeFieldReferenceExpression widthReference =
                new CodeFieldReferenceExpression(
                new CodeThisReferenceExpression(), "widthValue");
            constructor.Statements.Add(new CodeAssignStatement(widthReference,
                new CodeArgumentReferenceExpression("width")));
            CodeFieldReferenceExpression heightReference =
                new CodeFieldReferenceExpression(
                new CodeThisReferenceExpression(), "heightValue");
            constructor.Statements.Add(new CodeAssignStatement(heightReference,
                new CodeArgumentReferenceExpression("height")));
            targetClass.Members.Add(constructor);
        }
        /// <summary> 
        /// Add an entry point to the class. 
        /// </summary> 
        public void AddEntryPoint()
        {
            CodeEntryPointMethod start = new CodeEntryPointMethod();
            CodeObjectCreateExpression objectCreate =
                new CodeObjectCreateExpression(
                new CodeTypeReference("CodeDOMCreatedClass"),
                new CodePrimitiveExpression(5.3),
                new CodePrimitiveExpression(6.9));
            // Add the statement: 
            // "CodeDOMCreatedClass testClass =  
            //     new CodeDOMCreatedClass(5.3, 6.9);"
            start.Statements.Add(new CodeVariableDeclarationStatement(
                new CodeTypeReference("CodeDOMCreatedClass"), "testClass",
                objectCreate));
            // Creat the expression: 
            // "testClass.ToString()"
            CodeMethodInvokeExpression toStringInvoke =
                new CodeMethodInvokeExpression(
                new CodeVariableReferenceExpression("testClass"), "ToString");
            // Add a System.Console.WriteLine statement with the previous  
            // expression as a parameter.
            start.Statements.Add(new CodeMethodInvokeExpression(
                new CodeTypeReferenceExpression("System.Console"),
                "WriteLine", toStringInvoke));
            targetClass.Members.Add(start);
        }
        /// <summary> 
        /// Generate CSharp source code from the compile unit. 
        /// </summary> 
        /// <param name="filename">Output file name</param>
        public void GenerateCSharpCode()
        {
            CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
            var options = new CodeGeneratorOptions(){
                BracingStyle = "C",
                IndentString = "'t", BlankLinesBetweenMembers = true};
            using (var sourceWriter = new StringWriter())
            {
            foreach(CodeNamespace @namespace in targetUnit.Namespaces){
                foreach(CodeTypeDeclaration type in @namespace.Types){
                    var items = new List<CodeTypeMember>();
                    foreach(CodeTypeMember codeMember in type.Members){
                        var property = codeMember as CodeMemberProperty;
                        if(property == null){ 
                            items.Add(codeMember);
                            continue;}
                        items.Add(new CodeSnippetTypeMember(GetPropertyTextWithGetSetLevelDebuggerNonUserCodeAttribute(provider, options, sourceWriter, property)));
                    }
                    type.Members.Clear();
                    type.Members.AddRange(items.ToArray());
                }
            }
            }
            using (StringWriter sourceWriter = new StringWriter())
            {
                provider.GenerateCodeFromCompileUnit(
                    targetUnit, sourceWriter, options);
                sourceWriter.ToString().Dump();
            }
        }
        private static string GetPropertyTextWithGetSetLevelDebuggerNonUserCodeAttribute(CodeDomProvider provider, CodeGeneratorOptions options, StringWriter sourceWriter, CodeMemberProperty property)
        {
            provider.GenerateCodeFromMember(property, sourceWriter, options);
            var code = sourceWriter.ToString();
            sourceWriter.GetStringBuilder().Clear();
            var lines = code.Split(new[] { Environment.NewLine }, StringSplitOptions.None).ToList();
            lines.RemoveAt(0);
            lines.RemoveAt(lines.Count -1);
            for (var i = lines.Count() - 1; i >= 0; i--)
            {
                var line = lines[i];
                lines[i] = "'t't't" + line;
                if (line.TrimStart() == "get" || line.TrimStart() == "set")
                {
                    //Insert attribute above
                    lines.Insert(i, "'t't't[System.Diagnostics.DebuggerNonUserCode()]");
                }
            }
            return String.Join(Environment.NewLine, lines.ToArray());
        }
// Define other methods and classes here
}

生成这个类:

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.34209
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace CodeDOMSample
{
  using System;

  public sealed class CodeDOMCreatedClass
  {
    // The width of the object.
    private double widthValue;
    // The height of the object.
    private double heightValue;
      // The Width property for the object.
      public double Width
      {
      [System.Diagnostics.DebuggerNonUserCode()]
        get
        {
          return this.widthValue;
        }
      [System.Diagnostics.DebuggerNonUserCode()]
        set
        {
          return this.widthValue;
        }
      }
      // The Height property for the object.
      public double Height
      {
      [System.Diagnostics.DebuggerNonUserCode()]
        get
        {
          return this.heightValue;
        }
      }
      // The Area property for the object.
      public double Area
      {
      [System.Diagnostics.DebuggerNonUserCode()]
        get
        {
          return (this.widthValue * this.heightValue);
        }
      }
    public CodeDOMCreatedClass(double width, double height)
    {
      this.widthValue = width;
      this.heightValue = height;
    }
    public override string ToString()
    {
      return string.Format("The object:'r'n width = {0},'r'n height = {1},'r'n area = {2}", this.Width, this.Height, this.Area);
    }
    public static void Main()
    {
      CodeDOMCreatedClass testClass = new CodeDOMCreatedClass(5.3D, 6.9D);
      System.Console.WriteLine(testClass.ToString());
    }
  }
}