【问题标题】:Generate private setters using CodeDOM使用 CodeDOM 生成私有 setter
【发布时间】:2014-08-25 21:32:04
【问题描述】:

我目前正在开发一个 API,它可以根据存储在 XML 文档中的一些预定义规则动态编译为程序集。

我很难让 CodeDOM 生成带有公共 getter 和私有 setter 并装饰有自定义属性的属性。

这就是我所追求的:

[Conditional()]
public E3477 E3477 { get; private set; }

但是我得到了这个,这不好,因为我不希望公开暴露二传手:

[Conditional()]
public E3477 E3477 
{
    get
    {
    }
    set
    {
    }
}

这是我正在使用的代码:

var componentRef = string.Format( "E{0}", component.XPathSelectElement( "Element" ).Value );
CodeMemberProperty prop = new CodeMemberProperty();
prop.Name = componentRef;
prop.Type = new CodeTypeReference( componentRef );
prop.HasSet = true;
prop.HasGet = true;
prop.Attributes = MemberAttributes.Public;
CodeAttributeDeclaration conditionalAttr = new CodeAttributeDeclaration( "Conditional" );
prop.CustomAttributes.Add( conditionalAttr );

compositeElementClass.Members.Add( prop );

我所追求的在 CodeDOM 中是否可行?

Stack Overflow 上的社区告诉我使用 CodeDom 并创建程序集,而不是使用 MSBuild,我最初尝试这样做但无法使其工作。

** 用难以阅读的代码编辑,看看是否可以简化 **

string GenerateDataElementsCode()
{
    CodeNamespace globalNamespace = new CodeNamespace();
    globalNamespace.Imports.Add( new CodeNamespaceImport( string.Format( "{0}.DataElements", _project.Target.RootNamespace ) ) );

    CodeCompileUnit unit = new CodeCompileUnit();

    unit.Namespaces.Add( globalNamespace );

    CodeNamespace ns = new CodeNamespace( string.Format( "{0}.DataElements", _project.Target.RootNamespace ) );

    var codesDoc = XDocument.Load( string.Format( @"{0}\{1}", _project.Source.RootPath, _project.Source.UNCL ) );

    var doc = XDocument.Load( string.Format( @"{0}\{1}", _project.Source.RootPath, _project.Source.EDED ) );
    foreach ( XNode node in doc.Descendants( "DataElement" ) )
    {
        CodeTypeDeclaration dataElementClass = new CodeTypeDeclaration()
        {
            Name = string.Format( "E{0}", node.XPathSelectElement( "Identifier" ).Value ),
            IsClass = true
        };

        dataElementClass.Comments.Add( new CodeCommentStatement( node.XPathSelectElement( "Description" ).Value, true ) );

        dataElementClass.BaseTypes.Add( "SimpleObject" );

        CodeAttributeDeclaration dataElementAttr = new CodeAttributeDeclaration( "DataElement" );
        dataElementAttr.Arguments.Add(
            new CodeAttributeArgument
                    {
                        Name = "",
                        Value = new CodePrimitiveExpression( node.XPathSelectElement( "Identifier" ).Value )
                    } );
        dataElementAttr.Arguments.Add(
            new CodeAttributeArgument
            {
                Name = "",
                Value = new CodePrimitiveExpression( node.XPathSelectElement( "Name" ).Value )
            } );
        dataElementAttr.Arguments.Add(
            new CodeAttributeArgument
            {
                Name = "",
                Value = new CodePrimitiveExpression( node.XPathSelectElement( "Description" ).Value )
            } );

        CodeAttributeDeclaration dataElementFormatAttr = new CodeAttributeDeclaration( "DataElementFormat" );
        dataElementFormatAttr.Arguments.Add(
            new CodeAttributeArgument
            {
                Name = "Cardinality",
                Value = new CodePrimitiveExpression( node.XPathSelectElement( "Cardinality" ).Value )
            } );

        dataElementClass.CustomAttributes.Add( dataElementAttr );
        dataElementClass.CustomAttributes.Add( dataElementFormatAttr );

        var codes = codesDoc.XPathSelectElements( "SimpleDataElements/SimpleDataElement/CodeLists/CodeList" ).Where( a => a.XPathSelectElement( "../../Code" ).Value == node.XPathSelectElement( "Identifier" ).Value );

        if ( codes.Count() > 0 )
        {

            CodeTypeDeclaration codesClass = new CodeTypeDeclaration( "Codes" );
            codesClass.Attributes = MemberAttributes.Static;
            codesClass.IsClass = true;

            foreach ( XNode codeNode in codes )
            {
                CodeMemberField con = new CodeMemberField( typeof( string ), string.Format( "Code{0}", codeNode.XPathSelectElement( "Code" ).Value ) );
                con.Attributes = MemberAttributes.Public | MemberAttributes.Const;
                con.InitExpression = new CodePrimitiveExpression( codeNode.XPathSelectElement( "Code" ).Value );

                con.Comments.Add( new CodeCommentStatement( codeNode.XPathSelectElement( "Description" ).Value, true ) );

                codesClass.Members.Add( con );
            }

            dataElementClass.Members.Add( codesClass );

        }

        ns.Types.Add( dataElementClass );

    }

    unit.Namespaces.Add( ns );

    var provider = new Microsoft.CSharp.CSharpCodeProvider();

    using ( var sourceCode = new StringWriter() )
    using ( var indentedTextWriter = new IndentedTextWriter( sourceCode, "    " ) )
    {
        // Generate source code using the code provider.
        provider.GenerateCodeFromCompileUnit( unit,
            indentedTextWriter,
            new CodeGeneratorOptions() { BracingStyle = "C" } );

        return sourceCode.GetStringBuilder().ToString();
    }
}

【问题讨论】:

  • 您是否考虑过简单地使用CSharpCodeProvider,例如my answer。它将允许您使用所需的 C# 源代码构建字符串并简单地动态编译它。
  • @KonradKokosa 是的,确实如此,但它变得太混乱了,因为我从 XML 文档中注入了值,而且变得混乱且难以遵循。

标签: c# codedom


【解决方案1】:

当有人说 CodeDOM 时,我是这样看的:

// create compiler
CodeDomProvider provider = CSharpCodeProvider.CreateProvider("C#");
CompilerParameters options = new CompilerParameters();
// add all loaded assemblies
options.ReferencedAssemblies.AddRange(
    AppDomain.CurrentDomain.GetAssemblies().Where(item => !item.IsDynamic).Select(item => item.Location).ToArray());
options.GenerateExecutable = false;
options.GenerateInMemory = true;
// source
string source = "using System;namespace Test{public class Test{";
source += "[Conditional()]public E3477 E3477 { get; private set; }";
source += ...
// compile
CompilerResults result = provider.CompileAssemblyFromSource(options, source);

【讨论】:

  • 我目前正在以与您展示的类似的方式添加源代码,但我正在使用 CodeDOM 对象生成类,然后将代码转换为字符串,然后再将其传递给编译器.我试着和你做的一样,但是它太混乱了,值被注入到字符串中,使得最终的代码难以理解。
  • @MikeClarke,“使用 CodeDOM 生成类,然后在将代码传递给编译器之前将其转换为字符串”?!听起来很复杂...
  • @KonradKokosa 我使用了一个教程来做到这一点;这就是我走那条路的原因。
  • Source as string 比您的代码更具可读性。如果它变得太乱(很多string.Format ?)那么简单地组织它,例如,使用扩展方法,它可以将给定的字符串修改为模板并产生想要的结果。很高兴看到final code hard to follow 的示例,也许它很容易让它更简单。
  • @Sinatr 我已经修改了问题以包含难以阅读的代码。
【解决方案2】:

好吧,如果你想使用 Codedom 对象,你可以创建一个 codememberfield 并在其名称中添加一个 getter/setter。

CodeMemberfield field = new CodeMemberField
  {
      Name = "YourPropertyName",
      Attributes = MemberAttributes.Public | MemberAttributes.Final,
      Type = new CodeTypeReference(typeof(YourClassName)),
  };

field.Name += " { get; private set; }";

https://stackoverflow.com/a/13679314/3496840

【讨论】:

    【解决方案3】:

    正如其他人所指出的,CodeDom 本身并不支持私有设置器(IIRC,因为它不是一个适用于 CodeDom 提供的所有语言的概念)。

    大多数解决方案建议将“{ get; private set; }”显式硬编码到 sn-p 表达式中,但如果您需要具有逻辑的访问器,这并没有真正的帮助。这是一种相关但不同的方法,可以让我解决类似的问题。它不漂亮,但确实有用:

    给定一些类似的帮助类

    public static class SnippetGenerator
    {
        private static CodeDomProvider codeGenProvider = CodeDomProvider.CreateProvider("CSharp");
        private static CodeGeneratorOptions codeGenOptions = new CodeGeneratorOptions() { BracingStyle = "C", BlankLinesBetweenMembers = false };
        private static StringWriter stringWriter = new StringWriter();
    
        public static string GenerateSnippet(CodeTypeMember member)
        {
            codeGenProvider.GenerateCodeFromMember(member, stringWriter, codeGenOptions);
            string snippet = stringWriter.ToString();
            stringWriter.GetStringBuilder().Length = 0;
            return snippet;
        }
    }
    

    然后您可以将现有的 CodeDom 属性转换为可以使用字符串操作进行后处理的 sn-ps:

    CodeMemberProperty componentProperty = new CodeMemberProperty();
    ... //(build out your property)
    
    // inject private scope onto setter
    string propertySnippet = SnippetGenerator.GenerateSnippet(componentProperty);
    propertySnippet = propertySnippet.Replace("  set\r\n", "  private set\r\n");
    CodeSnippetTypeMember propertySnippetMember = new CodeSnippetTypeMember(propertySnippet);
    

    注意:有可能比包含“\r\n”(根据您的世代/平台设置可能会有所不同)更好的狙击集合的方法,但这是一种确保它不是的简单方法抓取任何不正确的子字符串。选择最适合您项目的方式。

    【讨论】:

      猜你喜欢
      • 2015-04-25
      • 1970-01-01
      • 2015-08-03
      • 2011-01-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多