Press "Enter" to skip to content

3 – Codedom Introduzione all’uso parte

In questa terza parte della serie dedicata a CodeDom, vediamo nel dettaglio come abbiamo costruito la classe Helper che ci fornisce in forma semplice il modo di costruire i vari statement di base che costituiscono una classe, in questo helper vi sono alcuni metodi in più rispetto a quelli utilizzati veramente nell’esempio, che abbiamo inserito per darvi qualcosa in più per costruire poi il vostro generatore personalizzato.

La classe CodeDomHelper:

Questa classe, come già spiegato, è stata costruita per comodità, perché per quanto il CodeDom fornisca tutti gli strumenti per generare qualsiasi porzione di codice, le sue classi ed i suoi metodi sono a volte generici, perché gli Statement di un linguaggio di programmazione possono avere forma logica simile anche se aspetto diverso. L’helper ci permette esclusivamente di creare delle porzioni di codice con uno scopo definito, anche se magari non fanno altro che eseguire una riga di codice CodeDom generico. La comodità è di tipo pratico, il codice di un generatore di codice, una volta steso si modifica raramente, inoltre è un codice molto più vicino al codice strutturato procedurale che un vero codice OOP in quanto per generare una classe si chiama una lunga lista di statement. Pertanto avere una classe helper con nomi di metodi “potabili” ci aiuterà quando ci troveremo a dover intervenire per aggiungere un metodo o una proprietà o modificare una porzione di codice nel nostro generatore dopo sei mesi da quando lo abbiamo usato l’ultima volta.

La stesura dei metodi spiegati è effettuata in ordine alfabetico, quindi non c’è un ordine logico, se doveste trovarvi in difficoltà, usate il form di contatto e ponete i vostri quesiti.

GetCastedValue

        public static CodeCastExpression GetCastedValue(Type pType, CodeExpression pValue)
        {
            return (new CodeCastExpression(
                new CodeTypeReference(pType), pValue));
        }

Questo metodo crea una espressione di cast, ovvero trasforma un oggetto in un’altro. Il primo parametro da passare è il tipo di dato destinazione, il secondo parametro è l’espressione che sta sulla destra della funzione di Cast.

Questo metodo restituisce un espressione di questo tipo:

//C#
(string)(VariabileDaCastareAString);

 

'VB
 CType(VariabileDaCastareAString,String)

Siccome usualmente questo statement è la porzione di Destra di una espressione di assegnazione, ovviamente dovrà essere utilizzato per comporre una espressione di tipo GetVariableAssignment che troverete più avanti su questo articolo.

GetClass

        public static CodeTypeDeclaration GetClass(string pClassName)
        {
            return (new CodeTypeDeclaration(pClassName));
        }

Questo metodo, crea lo statement base di una classe quindi almeno una chiamata a questo metodo servirà in qualsiasi generatore di codice che crei una classe intera. Ciò che restituisce il GetClass è l’oggetto che da origine a questo codice:

//C#
public class TestClass
{
}

 

'VB
Public Class TestClass
End Class

E’ semplicemente una scatola vuota che sistemeremo all’interno della scatola “Namespace” una volta generata dovremo riempire gli spazi che essa contiene con altri oggetti. Se provate ad osservare l’oggetto CodeTypeDeclaration con l’object browser, troverete che l’oggetto contiene svariate property, che sono i contenitori che riempiremo con altri metodi di questa classe o del CodeDom stesso per riempirla di quello che è il suo codice. Alcuni di quelli che sono più utilizzati sono:

  • Attributes – una serie di MemberAttributes in OR che stabiliscono se la classe è privata, pubblica, interna; se è astratta, se è parziale ecc.  ritroveremo questa enumerazione anche su property e metodi.
  • BaseTypes – una CodeTypeReferenceCollection che contiene l’eventuale classe padre e le interfacce implementate dalla nostra classe.
  • Comments – una CodeCommentStatementCollection che contiene i commenti descrittivi che verranno inseriti in testa alla classe.
  • Is… – Alcune property boolean che ci dicono se il nostro oggetto è una classe, un interfaccia, una enumerazione.
  • Members – una CodeTypeMemberCollection che contiene tutti i membri della classe quindi campi, property, metodi e quant’altro possiamo inserire all’interno di una classe.
  • Name – Il Nome della classe
  • TypeAttributes una collezione di TypeAttributes che rappresenterà gli attributi che vorremo inserire come decorazione della classe.
  • TypeParameters – una CodeTypeParameterCollection che conterrà i parametri di tipo necessari alla generazione di una classe di tipo Generic.

Gli altri metodi della nostra classe helper assieme ai metodi del codedom, ci permetteranno di riempire questa scatola, soprattutto la collection Members della stessa.

GetConstructorBase

        public static CodeConstructor GetConstructorBase(MemberAttributes pAccess, string pDescription)
        {

            CodeConstructor cst = new CodeConstructor();
            cst.Attributes = pAccess;
            cst.Comments.AddRange(GetSummaryComments(pDescription));

            return cst;

        }

Questo metodo, genera il costruttore di base di una classe, un costruttore senza alcun parametro, l’oggetto costruito può poi essere ulteriormente modificato aggiungendovi le definizioni dei parametri, le variabili e tutti gli statement che deve contenere.

//C#

 

  public TestClass()
  {
  }

 

'VB
   Public Sub New()
        MyBase.New
   End Sub

Come l’oggetto restituito dalla GetClass è quindi una scatola vuota che deve essere riempita con ulteriori oggetti. Anche in questo caso, osservando l’oggetto CodeConstructor e le sue property possiamo trovare alcune di quelle che ci serviranno per inserirvi il contenuto. Vediamo alcuni dei più importanti:

  • BaseConstructorArgs – Argomenti da passare alla chiamata al costruttore di base se questo costruttore effettua un override dello stesso.
  • ImplementationTypes – Contiene gli eventuali metodi di interfacce implementati usualmente nel costruttore non si usano, ma vi ricordo che anche il costruttore è un metodo e il Codedom deriva la sua definizione dalla classe base di implementazione dei metodi.
  • Comments – Commenti che volessimo inserire per il metodo.
  • Parameters – La dichiarazione dei parametri.
  • Statements – Collezione ove inseriremo tutte le istruzioni del metodo.
  • TypeParameters – Collezione della definizione dei tipi per un generic.

Nell’uso comune da noi testato, abbiamo usato Comments, Parameters, Statements.

GetStartRegion e GetEndRegion

        public static CodeSnippetTypeMember GetStartRegion(string pRegionDescription)
        {
            CodeSnippetTypeMember region = new CodeSnippetTypeMember(string.Empty);
            region.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.Start, pRegionDescription));
            return (region);
        }

 

        public static CodeSnippetTypeMember GetEndRegion()
        {
            CodeSnippetTypeMember region = new CodeSnippetTypeMember(string.Empty);
            region.StartDirectives.Add(new CodeRegionDirective(CodeRegionMode.End, string.Empty));
            return (region);
        }

Questi due metodi ci permettono di generare una regione all’interno del codice per dare una struttura che può essere poi nascosta utilizzando il sistema di outlining dell’editor di Visual Studio. Le regioni inoltre servono a raccogliere in modo omogeneo le varie parti di una classe.

Il codice restituito da questi due metodi verrà aggiunto alla collezione Members della classe.

#region Nome della regione

#endregion

 

#Region "Nome della regione"

#End Region

GetFieldVariable

        public static CodeMemberField GetFieldVariable(string pName, string pTypeName
            , MemberAttributes pAccessLevel, string pDescription,
            CodeExpression pInitExpression)
        {
            CodeMemberField fld = new CodeMemberField(pTypeName, pName);
            fld.Attributes = pAccessLevel;
            if (pInitExpression != null)
            {
                fld.InitExpression = pInitExpression;
            } // if
            fld.Comments.AddRange(GetSummaryComments(pDescription));
            return fld;
        }

Questo metodo, genera un oggetto di tipo CodeMemberField, ovvero una variabile member per una classe, come potete notare da quanto riportato nel nostro codice modello semplice, per generare un CodeMemberField abbiamo bisogno di un Nome per la variabile e di un Nome di Tipo, inoltre useremo l’enumerazione MemberAttributes per definire quale è il suo livello di accesso, aggiungeremo un commento che la descrive ed eventualmente, una espressione che ne predispone il valore iniziale.

Per le variabili member è possibile inizializzare la variabile sia nella sua dichiarazione, sia nel costruttore. Io preferisco inizializzare le variabili nel costruttore, salvo alcuni casi particolari, ma è una questione di preferenze di scrittura del codice non una necessità specifica.

        /// <summary>
        /// Il Mio nome
        /// </summary>
        private string mMyName;

        /// <summary>
        /// Il Mio nome con init
        /// </summary>
        private string mMyNameInit = "<non inizializzato>";

 

        '''<summary>
        '''Il Mio nome
        '''</summary>
        Private mMyName As String

        '''<summary>
        '''Il Mio nome con init
        '''</summary>
        Private mMyNameInit As String = "<non inizializzato>"

Anche questa espressione, verrà aggiunta all’oggetto Classe che avremo generato come elemento della collezione Members della stessa. Un overload di questo metodo, senza il parametro di inizializzazione può rendere più pulito il codice di generazione.

GetFieldVariableAssignment (semplice)

        public static CodeAssignStatement GetFieldVariableAssignment(
            string pFieldName, CodePrimitiveExpression pValue)
        {
            return (new CodeAssignStatement(new CodeFieldReferenceExpression(
                new CodeThisReferenceExpression(), pFieldName), pValue));
        }

Questo metodo, ci permette di creare uno statement per assegnare un valore ad una delle variabili member di una classe. Infatti essendo la classe l’unica a manipolare in modo diretto le proprie variabili member, potrebbe avere la necessità di inizializzare queste variabili o assegnarvi un valore senza passare per la property corrispondente che potrebbe essere ReadOnly.

this.mMyName2 = Pippo;

 

Me.mMyName2 = Pippo

Come possiamo notare dal codice del metodo, utilizziamo un oggetto CodeFieldReferenceExpression per generare lo statement di assegnazione ed un CodeThisReferenceExpression per generare in modo esplicito il riferimento alla classe ovvero il this o il Me in base al linguaggio scelto. L’oggetto restituito sarà di tipo CodeAssignStatement e dovrà essere aggiunto alla collection Statements di un Costruttore, di un Metodo o di un qualsiasi altro contenitore di codice (una IF, un ciclo FOR o WHILE ecc.).

GetFieldVariableAssignment (con espressione)

        public static CodeAssignStatement GetFieldVariableAssignment(
                   string pFieldName, CodeExpression pValue)
        {
            return (new CodeAssignStatement(new CodeFieldReferenceExpression(
            new CodeThisReferenceExpression(), pFieldName), pValue));

        }

Come la precedente, ma invece di passare un semplice valore, gli passiamo un’espressione primitiva.

this.mMyName = "<non inizializzato>";

 

Me.mMyName = "<non inizializzato>"

GetFieldVariableAssignment (con generazione oggetto)

        public static CodeAssignStatement GetFieldVariableAssignment(
            string pFieldName, Type pFieldType, CodeExpression[] pParameters)
        {
            CodeTypeReference type = new CodeTypeReference(pFieldType);
            CodeObjectCreateExpression init = new CodeObjectCreateExpression(type, pParameters);
            return (new CodeAssignStatement(
                new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), pFieldName)
                , init));
        }

Come la precedente, ma generiamo un istanza di un oggetto (una espressione New).

this.mMyCornice = new string('-', 80);

 

Me.mMyCornice = New String(Global.Microsoft.VisualBasic.ChrW(45), 80)

GetLocalVariable

        public static CodeVariableDeclarationStatement GetLocalVariable(
            string pVariableName, Type pVariableType, CodePrimitiveExpression pValue)
        {
            CodeTypeReference type = new CodeTypeReference(pVariableType);
            return (new CodeVariableDeclarationStatement(type, pVariableName, pValue));
        }

Questo codice, similmente al codice per generare una variabile member, serve per generare invece una variabile locale all’interno di un metodo. Genera un CodeVariableDeclarationStatement a partire da un Nome, un Tipo e un valore da assegnare alla variabile. E’ possibile creare anche per questo metodo un Overload che permetta di creare un nuovo oggetto, usando lo stesso codice e gli stessi parametri usati per la dichiarazione dell’assegnazione dei campi di una variabile member.

string myString = "Nuova variabile locale";
int myInt = 80;

 

Dim myString As String = "Nuova variabile locale"
Dim myInt As Integer = 80

GetMClassName

        public static CodeMemberField GetMClassName()
        {
            CodeMemberField fld = new CodeMemberField(typeof(string), "mClassName");
            fld.Attributes = MemberAttributes.Private|MemberAttributes.Static;
            fld.InitExpression = GetMClassNameExpression();
            fld.Comments.AddRange(GetSummaryComments(
		"Indica il nome della classe predisposta per la gestione delle eccezioni"));
            return fld;
        }

Questo metodo, che va sempre assieme al successivo, produce la famigerata variabile mClassName che troverete in tutte le mie classi e in tutti i miei articoli, il suo scopo è quello di fornire il nome di una classe senza che siamo costretti a scriverlo a manina, in modo che se per caso le cambiamo nome fra sei mesi o un anno non dobbiamo ricordarci di modificare anche mClassName.

GetMClassNameExpression

        public static CodeSnippetExpression GetMClassNameExpression()
        {
            return GetPlainCode("System.Reflection.MethodBase.GetCurrentMethod().ReflectedType.Name");
        }

Una scorciatoia di tipo “BruteForce”, anche se nel codedom esiste certamente il necessario a generare questo codice, abbiamo optato per un metodo Brutale (ma efficace) per evitare di andare a cercare cose troppo raffinate nelle classi di CodeDom.

private static string mClassName = 
    System.Reflection.MethodBase.GetCurrentMethod().ReflectedType.Name;

 

Private Shared mClassName As String _
    = System.Reflection.MethodBase.GetCurrentMethod().ReflectedType.Name

Il valore restituito dalla GetMClassName è di tipo CodeMemberField esattamente come le normali variabili member abbiamo solo utilizzato una OR per gli attributi in modo da definire la variabile come Statica (Shared) quindi una sola variabile mClassName per tutte le classi di un certo tipo.

GetMethod

        public static CodeMemberMethod GetMethod(string pMethodName, string pMethodType,
            string pDescription)
        {
            CodeMemberMethod method = new CodeMemberMethod();
            method.Name = pMethodName;
            method.Comments.AddRange(GetSummaryComments(pDescription));
            if (pMethodType != string.Empty)
            {
                method.ReturnType = new CodeTypeReference(pMethodType);
            } // if
            return method;
        }

Questo metodo, genera un metodo (e scusate se sembra un giroingiro), In questo caso, passiamo i minimi dati indispensabili, quindi il nome, una stringa che ne rappresenta il tipo, la descrizione. Gli attributi, il contenuto, i parametri eventualmente da fornirgli non vengono forniti direttamente qui ma saranno aggiunti al CodeMemberMethod generato da questo metodo dopo la sua generazione, usando le funzioni per generare gli statements, i parametri, le variabili e quant’altro necessario.

        private void MetodoNuovo(string pParametro)
        {
        }

 

        Private Sub MetodoNuovo(ByVal pParametro As String)
        End Sub

Il codice qui sopra generato usando il nostro progetto demo, oltre al metodo GetMethod, aggiorna gli Attributes del metodo, e aggiunge un parametro alla collezione Parameters.

GetMethodCall

        public static CodeMethodInvokeExpression GetMethodCall(string pObjectName,
            string pMethodName, CodeExpression[] pParameters)
        {
            return (new CodeMethodInvokeExpression(GetPlainCode(pObjectName),
                pMethodName, pParameters));

        }

Questo metodo crea una chiamata ad un metodo di una classe, oltre al nome del metodo, richiede che indichiamo l’oggetto a cui fare riferimento (Es. Miaclasse.MioMetodo()), e la lista dei parametri da passare al metodo stesso.

this.SecondoMetodo();

 

Me.SecondoMetodo

Il codice generato è questo, attenzione che visto che abbiamo usato una stringa per l’object name, è necessario usare una IF lavorando in VB piuttosto che in C# è ovviamente possibile creare un overload più intelligente di questo per le chiamate senza riferimento ad oggetto e quelle con riferimento ad un This statement.

GetNamespace (con Nome)

        public static CodeNamespace GetNamespace(string pNamespace)
        {
            if (pNamespace != null)
            {
                return new CodeNamespace(pNamespace);
            }
            else
            {
                return new CodeNamespace();
            }
        }

Questo metodo ed il successivo forniscono l’oggetto base che poi sarà passato al generatore di codice. Un Namespace deve sempre contenere una classe per poterla generare correttamente, in C# il namespace senza nome sarà usato unicamente per le clausole using, in VB invece può essere necessario se la classe è definita a livello di Rootnamespace del progetto. La versione con nome produce il codice seguente.

namespace Dnw.Test
{

}

 

Namespace Dnw.Test
End Namespace

GetNamespace (senza alcun nome)

        public static CodeNamespace GetNamespace()
        {
            return (GetNamespace(null));

        }

Questo metodo produce un oggetto importantissimo che non da origine ad alcun codice di per se, ma è il contenitore base per il codice degli Using statements in C# per gli import statements in VB o per una classe inserita sul RootNamespace in VB.

non genera codice

 

non genera codice

GetNewObjExpression

        public static CodeObjectCreateExpression GetNewObjExpression(
             Type pType, CodeExpression[] pParameters)
        {
            CodeTypeReference type = new CodeTypeReference(pType);
            return (new CodeObjectCreateExpression(type, pParameters));
        }

Questo metodo permette di creare un istanza di un oggetto, passandogli il tipo dell’oggetto e gli eventuali parametri per generarlo.

new System.Data.DataTable("Nometabella");

 

 New System.Data.DataTable("Nometabella")

GetParameter

        public static CodeParameterDeclarationExpression GetParameter(Type pType, string pName)
        {
            return (new CodeParameterDeclarationExpression(pType, pName));
        }

Questo metodo permette di creare la dichiarazione di un parametro da aggiungere alla collezione parameters di un metodo.

private void MetodoNuovo(string pParametroString, System.@int pParametroInt, 
    System.Data.DataTable pParametroDataTable)
{
}

 

Private Sub MetodoNuovo( _
    ByVal pParametroString As String, _ 
    ByVal pParametroInt As System.int, _
    ByVal pParametroDataTable As System.Data.DataTable)
End Sub

Nell’esempio abbiamo generato tre diversi tipi di parametro.

GetPlainCode

        public static CodeSnippetExpression GetPlainCode(string pCode)
        {
            return (new CodeSnippetExpression(pCode));
        }

Il metodo per la generazione di tipo “Brute Force” ovvero la stringa passata diviene codice così com’è.

string pippo = "test stringa";
GeneraQualcosaConUnMetodo();

 

string pippo = "test stringa"
GeneraQualcosaConUnMetodo()

GetProperty (Con espressione specifica per Get e Set)

        public static CodeMemberProperty GetProperty(string pName, CodeExpression pGet
            , CodeExpression pSet, Type pType, string pDescription,
             bool pHasGet, bool pHasSet)
        {

            CodeMemberProperty prop = new CodeMemberProperty();
            prop.Name = pName;
            prop.Comments.AddRange(GetSummaryComments(pDescription));
            prop.Attributes = MemberAttributes.Public;
            prop.Type = new CodeTypeReference(pType);
            prop.HasGet = pHasGet;
            prop.HasSet = pHasSet;
            if (pHasGet)
            {
                prop.GetStatements.Add(
                    new CodeMethodReturnStatement(pGet));
            } // if
            if (pHasSet)
            {
                prop.SetStatements.Add(
                    new CodeAssignStatement(pSet, GetPlainCode("value")));
            } // if
            return prop;

        }

Questo metodo genera una property completa delle espressioni di Get e Set, le espressioni passate potrebbero essere anche qualcosa di complesso.

        public string City
        {
            get
            {
                return GetTheCity();
            }
            set
            {
                SetTheCity = value;
            }
        }

 

        Public Property City() As String
            Get
                Return GetTheCity()
            End Get
            Set
                SetTheCity = value
            End Set
        End Property

GetProperty (Con espressione standard per Get e Set)

        public static CodeMemberProperty GetProperty(string pName, 
             string pField, Type pType, string pDescription,
             bool pHasGet, bool pHasSet)
        {
            CodeMemberProperty prop = new CodeMemberProperty();
            prop.Name = pName;
            prop.Comments.AddRange(GetSummaryComments(pDescription));
            prop.Attributes = MemberAttributes.Public;
            prop.Type = new CodeTypeReference(pType);
            prop.HasGet = pHasGet;
            prop.HasSet = pHasSet;

            if (pHasGet)
            {
                prop.GetStatements.Add(
                    new CodeMethodReturnStatement(
                    new CodeArgumentReferenceExpression(pField)));
            } // if
            if (pHasSet)
            {
                prop.SetStatements.Add(
                    GetFieldVariableAssignment(
                    pField, GetPlainCode("value")));
            } // if
            return prop;

        }

Questo secondo overload crea automaticamente il get e il set della variabile esposta dalla property.

        public virtual string FamilyName
        {
            get
            {
                return mFamilyName;
            }
            set
            {
                this.mFamilyName = value;
            }
        }

 

        Public Overridable Property FamilyName() As String
            Get
                Return mFamilyName
            End Get
            Set
                Me.mFamilyName = value
            End Set
        End Property

GetProperty (Senza contenuto per modifica successiva)

        public static CodeMemberProperty GetProperty(string pName, Type pType, string pDescription)
        {
            CodeMemberProperty prop = new CodeMemberProperty();
            prop.Name = pName;
            prop.Comments.AddRange(GetSummaryComments(pDescription));
            prop.Attributes = MemberAttributes.Public;
            prop.Type = new CodeTypeReference(pType);
            return prop;

        }

Questo metodo crea solo lo scheletro della property che poi potrà essere riempito con quanto necessario usando gli oggetti messi a disposizione dalla classe CodeMemberProperty.

        public virtual string Name
        {
        }

 

        Public Overridable Property Name() As String
        End Property

GetString

        public static CodePrimitiveExpression GetString(string pValue)
        {
            return (new CodePrimitiveExpression(string.Format("{0}", pValue)));
        }

Un metodo che sembra strano, però se dobbiamo inserire una stringa nel codice non possiamo usare direttamente il GetPlainCode oppure dovremmo stare a passare delimiters ed altro. Questo metodo ci crea lo snippet di codice per una stringa. Unendo il metodo GetLocalVariabile e il metodo GetString, possiamo ottenere il codice qui sotto:

string ilMioNome = "Mi chiamo Mario";

 

Dim ilMioNome As String = "Mi chiamo Mario"

GetSummaryComments

        public static CodeCommentStatementCollection GetSummaryComments(string pDescription)
        {
            CodeCommentStatementCollection summary = new CodeCommentStatementCollection();
            summary.Add(new CodeCommentStatement("<summary>", true));
            summary.Add(new CodeCommentStatement(pDescription, true));
            summary.Add(new CodeCommentStatement("</summary>", true));
            return summary;

        }

Un metodo per fare ciò che non piace per nulla ai programmatori 😀 inserire i commenti in testa ai metodi, alle classi, alleproperty eccetera. La funzione crea specificamente un commento di tipo XML Summary ma ovviamente potete generare qualsiasi cosa.

	/// <summary>
         /// Classe per il test dei metodi del codedom
         /// </summary>

 

	'''<summary>
         '''Classe per il test dei metodi del codedom
         '''</summary>

Nella chiamata al metodo CodeCommentStatement faccio solo notare che il parametro boolean se posto a true crea un commento di tipo XML se posto a false crea un commento normale.

GetTypeOf (Da tipo dato)

        public static CodeTypeOfExpression GetTypeOf(Type pType)
        {
            return new CodeTypeOfExpression(GetTypeReference(pType));

        }

(da nome dato)

        public static CodeTypeOfExpression GetTypeOf(string pTypeName)
        {
            return new CodeTypeOfExpression(pTypeName);
        }

Queste due versioni dello stesso metodo, ci permettono di ottenere un espressione di tipo typeof o GetType, che è indispensabile utilizzare in varie funzionalità ed espressioni generate dalle nostre classi.

typeof(int);

 

GetType(Integer)

GetTypeReference

        public static CodeTypeReference GetTypeReference(Type pType)
        {
            return (new CodeTypeReference(pType));
        }

Questo metodo che sembra uguale al precedente ma non lo è invece serve per ottenere un riferimento ad un tipo, ovvero il necessario a scrivere una espressione di cast, Infatti lo troviamo utilizzato nel metodo GetCastExpression della nostra classe Helper. Il codice che produce è la parte sinistra dell’espressione di cast in C#. E l’espressione a destra del CType di VB.

((System.Text.StringBuilder)VariabileDaConvertire);

 

CType(VariabileDaConvertire,System.Text.StringBuilder)

GetUsing

        public static CodeNamespaceImport GetUsing(string pNamespaceToImport)
        {
            return (new CodeNamespaceImport(pNamespaceToImport));
        }

Questo metodo ha lo scopo di produrre una espressione di using o imports per importare e definire gli alias dei namespaces referenziati all’interno di una classe C# o VB. Ricordiamo che in C# è indispensabile definire uno specifico namespace senza nome ove inserire queste clausole per far si che siano esterne alla definizione della classe.

using System.Data;
using System.Text;
using System.Windows.Forms;

 

Imports System.Data
Imports System.Text
Imports System.Windows.Forms

GetValue

        public static CodePrimitiveExpression GetValue(object pObj)
        {
            return (new CodePrimitiveExpression(pObj));
        }

Questo metodo ci serve per generare un espressione che rappresenta il valore di una variabile o di una espressione binaria in generale. Ovunque abbiamo bisogno di passare un oggetto e inserirne il valore nel codice generato dobbiamo usare questo tipo di chiamata. nell’esempio abbiamo usato questo metodo per generare il valore a destra di una espressione condizionale (il valore 12)

if (pippo > 12)
{
}

 

If (pippo > 12) Then
End If

GetVariableAssignment (Nome+Espressione)

        public static CodeAssignStatement GetVariableAssignment(
            string pVariableName, CodeExpression pValue)
        {
            return (new CodeAssignStatement(GetPlainCode(pVariableName), pValue));
        }

Metodo per generare una istruzione che assegna un valore ad una variabile.

pluto = 12;

 

pluto = 12

GetVariableAssignment (Nome, EspressionePrimitiva )

        public static CodeAssignStatement GetVariableAssignment(
            string pVariableName, CodePrimitiveExpression pValue)
        {
            return (new CodeAssignStatement(GetPlainCode(pVariableName), pValue));
        }

Metodo per generare una istruzione che assegna una espressione ad una variabile.

paperino = (24 + 1460);

 

paperino = (24 + 1460)

GetVariableAssignment(Nome, tipo, espressione multi parametrica)

        public static CodeAssignStatement GetVariableAssignment(
                string pVariableName, Type pVariableType, CodeExpression[] pParameters)
        {
            CodeObjectCreateExpression init = GetNewObjExpression(pVariableType, pParameters);
            return (new CodeAssignStatement(GetPlainCode(pVariableName), init));

        }

metodo per generare una istruzione che assegna ad una variabile una nuova istanza di un oggetto con eventuali parametri ad esso passati.

paperinik = new Dnw.Test.Persona("Mario", "Rossi");

 

paperinik = new Dnw.Test.Persona("Mario", "Rossi")

Con i tre overload del metodo per generare l’assegnazione di una variabile siamo in grado di coprire ogni tipo di assegnazione. Ricordo che l’espressione del secondo tipo di metodo può essere qualsiasi cosa, non necessariamente una espressione numerica come in questo caso.

Con questo metodo, si concludono i metodi costruttivi del nostro Helper, ovvero quelli che servono per costruire l’oggetto che darà origine al codice, quello che in gergo tecnico viene chiamato il Grafo della classe. Di seguito invece sono i metodi per creare fisicamente il codice a partire dal grafo.

GenerateCodeUnit

        public static CodeCompileUnit GenerateCodeUnit(
                                    CodeNamespace pUsingNamespace, CodeNamespace pCodeNamespace)
        {
            CodeCompileUnit codeUnit = new CodeCompileUnit();

            codeUnit.Namespaces.Add(pUsingNamespace);
            codeUnit.Namespaces.Add(pCodeNamespace);
            return (codeUnit);
        }

Questo metodo crea l’oggetto che proguce il codice, vi aggiunge i Namespaces che contengono la classe o le classi che abbiamo generato e lo mette a disposizione del generatore di codice vero e proprio.

GenerateCSharpCode

        public static void GenerateCSharpCode(string pFileName,
            CodeNamespace pUsingNamespace, CodeNamespace pCodeNamespace)
        {

            //Predispone il provider
            CSharpCodeProvider provider = new CSharpCodeProvider();

            //Predispone le opzioni di formattazione della classe
            CodeGeneratorOptions codeOpts = new CodeGeneratorOptions();
            codeOpts.BracingStyle = "C";
            codeOpts.IndentString = "\t";
            // Usare true una volta costruito tutto x avere l'ordine giusto
            codeOpts.VerbatimOrder = true;
            codeOpts.BlankLinesBetweenMembers = true;

            FileInfo fInfo = new FileInfo(pFileName);
            DirectoryInfo dInfo = new DirectoryInfo(fInfo.DirectoryName);
            if (!dInfo.Exists)
            {
                dInfo.Create();
            }

            //Genera il codedom container
            CodeCompileUnit unit = GenerateCodeUnit(pUsingNamespace, pCodeNamespace);

            //Crea uno stream writer per il file di output
            IndentedTextWriter tw = new IndentedTextWriter(
                    new StreamWriter(pFileName, false), "\t");

            //genera il codice sorgente usando lo stream e le opzioni predisposte
            provider.GenerateCodeFromCompileUnit(unit, tw, codeOpts);
            //Chiude il file di output
            tw.Close();

        }

Questo metodo produce il codice in linguaggio C# e lo scrive sul file da noi desiderato. Come potete vedere, al generatore è possibile indicare tutta una serie di opzioni che permettono di formattare il codice generato in modo che sia leggibile una volta scritto sul file di testo. Il CodeProvider che abbiamo usato, non fa parte di System come tutti gli oggetti di CodeDom, ma si trova in Microsoft.CSharp.

GenerateVbCode

        public static void GenerateVbCode(string pFileName,
            CodeNamespace pUsingNamespace, CodeNamespace pCodeNamespace)
        {

            //Predispone il provider
            Microsoft.VisualBasic.VBCodeProvider provider = new Microsoft.VisualBasic.VBCodeProvider();

            //Predispone le opzioni di formattazione della classe
            CodeGeneratorOptions codeOpts = new CodeGeneratorOptions();
            codeOpts.BracingStyle = "C";
            codeOpts.IndentString = "\t";
            codeOpts.VerbatimOrder = true; // Usare true una volta costruito tutto x avere l'ordine giusto
            codeOpts.BlankLinesBetweenMembers = true;

            //Genera il codedom container
            CodeCompileUnit unit = GenerateCodeUnit(pUsingNamespace, pCodeNamespace);

            FileInfo fInfo = new FileInfo(pFileName);
            DirectoryInfo dInfo = new DirectoryInfo(fInfo.DirectoryName);
            if (!dInfo.Exists)
            {
                dInfo.Create();
            }

            //Crea uno stream writer per il file di output
            IndentedTextWriter tw = new IndentedTextWriter(
                    new StreamWriter(pFileName, false), "\t");

            //genera il codice sorgente usando lo stream e le opzioni predisposte
            provider.GenerateCodeFromCompileUnit(unit, tw, codeOpts);
            //Chiude il file di output
            tw.Close();
        }

Questo metodo, pure se scritto in C# 😀 genera la classe in formato VB, vi invito a notare come il Code Provider che fornisce il necessario a scrivere fisicamente il codice su file si trovi in Microsoft.VisualBasic.

Perché vi ho fatto notare come gli oggetti “CodeProvider” non si trovino sul namespace System ma sul Namespace Microsoft, semplicemente perché Anche se il framework è standard e la sua specifica è pubblica, i linguaggi C# e VB sono nella versione implementata da Microsoft, Per chi utilizza SharpDeveloper, il CodeProvider C# sarà quello creato dalla community opensource, per chi usa il C# di Borland o usa Delphi, lo stesso ci sarà uno specifico code provider implementato dalla casa produttrice.

Conclusioni

Ovviamente questi tre articoli non sono che un modo per scalfire la superficie della montagna del CodeDom, mi sono limitata a mostrare che Si può fare, e che abbiamo a disposizione tutti i mattoncini Lego che possono servirci per crearci dei generatori che producano oggetti utili al nostro lavoro per evitare di sprecare tempo in compiti ripetitivi.

Questi articoli hanno preso in considerazione solo elementi di base, tutti contenuti nel framework 2.0 speriamo possano aiutare chi legge a rompere il ghiaccio e se vorrete estendere questo codice e condividerlo con la community ovviamente siete i benvenuti.

Il Progetto esempio con tutto il codice per i 3 articoli di questa serie può essere scaricato al link seguente:

Per qualsiasi domanda, curiosità approfondimento, o se trovate un errore usate pure i l link al modulo di contatto in cima alla pagina.