簡體   English   中英

使用 CodeDOM 生成私有 setter

[英]Generate private setters using CodeDOM

我目前正在開發一個 API,該 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();
    }
}

當有人說 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 本身並不支持私有設置器(IIRC,因為它不是一個適用於 CodeDom 服務的所有語言的概念)。

大多數解決方案都建議將 "{ get; private set; }" 明確地硬編碼到一個片段表達式中,但是如果您需要具有邏輯的訪問器,這並沒有真正的幫助。 這是一種相關但不同的方法,可以讓我解決類似的問題。 它不漂亮,但有訣竅:

給定一些幫助類

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 屬性轉換為可以使用字符串操作進行后處理的片段:

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" 更好的狙擊設置的方法(根據您的世代/平台設置可能會有所不同),但這是一種確保它不會抓取任何錯誤的簡單方法子串。 選擇最適合您的項目的任何方式。

好吧,如果您想使用 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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM