如何使用codedom的CodeMemberProperty为属性getter或setter生成DebuggerStep
本文关键字:getter setter 生成 DebuggerStep 属性 何使用 codedom CodeMemberProperty | 更新日期: 2023-09-27 18:09:51
我如何生成一个DebuggerStepThroughAttribute在getter/setter与CodeDOM?
这个问题来自MSDN文档和StackOverflow上的一个问题。
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());
}
}
}