DEV485.NET CodeDOM demystified Beat Schwegler Architect Evangelist.NET Developer Group Microsoft...
-
Upload
juniper-fields -
Category
Documents
-
view
219 -
download
1
Transcript of DEV485.NET CodeDOM demystified Beat Schwegler Architect Evangelist.NET Developer Group Microsoft...
DEV485
.NET CodeDOM demystified
Beat Schwegler
Architect Evangelist
.NET Developer Group
Microsoft Corporation
Agenda
CodeDOMIntroduction
Assembly compilation
Source code generation
Source code parsing
Advanced conceptsTemplate based source code generation
On-the-fly proxy generation
CodeDOM scenarios
.NET Framework scenariosASP.NET assembly generation
WSDL proxy generation
Managed C++ WinForms designer
VS code wizards
Custom scenariosGeneration of typesafe collections
Source code skeleton generation
On the fly tie-pattern generation
CodeDOM namespaces
System.CodeDom Contains the Code Document Object Model (CodeDOM) graph types
System.CodeDom.CompilerContract for all CodeDomProviders
Contains interfaces and (abstract) base classes
CodeDOM providers
Microsoft providersC#, VB.NET, Managed C++, Jscript, J#
Microsoft partnersNetCOBOL (Fujitsu)
ActivePERL (ActiveState)
Eiffel (Interactive Software Engineering)
…
HelloWorld CodeDom graph
namespace Samples{ using System; public class Hello { public void SayHello() { Console.WriteLine("Hello World!"); } }}
CompileUnitCompileUnit
NamespacesNamespaces
Namespace Sample Namespace Sample
ImportsImports TypesTypes
Class HelloWorldClass HelloWorld
MembersMembers
Method SayHelloMethod SayHello
StatementsStatements
ExpressionExpression
SystemSystem
CodeDOM graphIndependent of programming language
In-memory representation of a source code document
Tree based object model
Doesn’t support all language specific features (no unsafe, no variable declaration lists, …)
Compiles if the root element is of type CodeCompileUnit
CodeDOM concepts
public static void Main(String[] args ){ String usr; FileStream f; StreamWriter w; try { usr=Environment.GetEnvironmentVariable("USERNAME"); f=new FileStream(“C:\\test.txt",FileMode.Create); w=new StreamWriter(f); w.WriteLine(usr); w.Close(); } catch (Exception e){ Console.WriteLine("Exception:"+e.ToString()); }}
public static void Main(String[] args ){ String usr; FileStream f; StreamWriter w; try { usr=Environment.GetEnvironmentVariable("USERNAME"); f=new FileStream(“C:\\test.txt",FileMode.Create); w=new StreamWriter(f); w.WriteLine(usr); w.Close(); } catch (Exception e){ Console.WriteLine("Exception:"+e.ToString()); }}
Source codeSource code
CompileUnitCompileUnit
NamespacesNamespaces
Namespace Sample Namespace Sample
ImportsImports TypesTypes
Class HelloWorldClass HelloWorld
MembersMembers
Method SayHelloMethod SayHello
StatementsStatements
ExpressionExpression
SystemSystem
CodeDOM graphCodeDOM graph
AssemblyAssembly
compilingcompilingCodeDOM graphCodeDOM graph
compilingcompilingsource codesource code
parsingparsingsource codesource code
generatinggeneratingsource codesource code
Programming model
Creating the code providerCodeDomProvider provider = new Microsoft.CSharp.CSharpCodeProvider();
Obtaining the required interfaceICodeGenerator generator = provider.CreateGenerator();
Calling the method
generator.GenerateCodeFromCompileUnit(…);
CodeDOM
Compiling an assembly on the flyIn-memory
stored to disk
Generating code according to an in-memory CodeDOM graph
Several code providers are already available (C#, VB.NET, J#, Managed C++)
Parsing code to get an in-memory CodeDOM graph
Agenda
CodeDOMIntroduction
Assembly compilation
Source code generation
Source code parsing
Advanced conceptsTemplate based source code generation
On-the-fly proxy generation
Assembly compilation
public static void Main(String[] args ){ String usr; FileStream f; StreamWriter w; try { usr=Environment.GetEnvironmentVariable("USERNAME"); f=new FileStream(“C:\\test.txt",FileMode.Create); w=new StreamWriter(f); w.WriteLine(usr); w.Close(); } catch (Exception e){ Console.WriteLine("Exception:"+e.ToString()); }}
public static void Main(String[] args ){ String usr; FileStream f; StreamWriter w; try { usr=Environment.GetEnvironmentVariable("USERNAME"); f=new FileStream(“C:\\test.txt",FileMode.Create); w=new StreamWriter(f); w.WriteLine(usr); w.Close(); } catch (Exception e){ Console.WriteLine("Exception:"+e.ToString()); }}
Source codeSource code
CompileUnitCompileUnit
NamespacesNamespaces
Namespace Sample Namespace Sample
ImportsImports TypesTypes
Class HelloWorldClass HelloWorld
MembersMembers
Method SayHelloMethod SayHello
StatementsStatements
ExpressionExpression
SystemSystem
CodeDOM graphCodeDOM graph
AssemblyAssembly
compilingcompilingCodeDOM graphCodeDOM graph
compilingcompilingsource codesource code
parsingparsingsource codesource code
generatinggeneratingsource codesource code
Assembly compilation
MotivationsOn-the-fly code generation and compilation
Interface interception
Compiled “scripting” support
ICodeCompiler (1/3)
Compilation sourcesCodeCompileUnit (CodeDOM graph)Source fileString containing source code
Compilation methodsSingle compilationCompileAssemblyFromDom(…)CompileAssemblyFromFile(…)CompileAssemblyFromSource(…)
Batch compilationCompileAssemblyFromDomBatch(…)CompileAssemblyFromFileBatch(…)CompileAssemblyFromSourceBatch(…)
ICodeCompiler (2/3)
Compilation parameterizationclass CompilerParameters
Compiler options
GenerateExecutable (false => dll)
GenerateInMemory
OutputAssembly
MainClass
…
ICodeCompiler (3/3)
Compilation resultsCompileAssemblyFrom… methods return CompileResults
CompiledAssembly, Errors, Output, …
Printing the compiler output to the screen:
CompilerResults results = compiler.CompileAssemblyFromFile(options, src);
foreach (string line in results.Output) { Console.WriteLine(line); }
Generating Generating assemblies on the flyassemblies on the fly
demodemo
Agenda
CodeDOMIntroduction
Assembly compilation
Source code generation
Source code parsing
Advanced conceptsTemplate based source code generation
On-the-fly proxy generation
Source code generation
public static void Main(String[] args ){ String usr; FileStream f; StreamWriter w; try { usr=Environment.GetEnvironmentVariable("USERNAME"); f=new FileStream(“C:\\test.txt",FileMode.Create); w=new StreamWriter(f); w.WriteLine(usr); w.Close(); } catch (Exception e){ Console.WriteLine("Exception:"+e.ToString()); }}
public static void Main(String[] args ){ String usr; FileStream f; StreamWriter w; try { usr=Environment.GetEnvironmentVariable("USERNAME"); f=new FileStream(“C:\\test.txt",FileMode.Create); w=new StreamWriter(f); w.WriteLine(usr); w.Close(); } catch (Exception e){ Console.WriteLine("Exception:"+e.ToString()); }}
Source codeSource code
CompileUnitCompileUnit
NamespacesNamespaces
Namespace Sample Namespace Sample
ImportsImports TypesTypes
Class HelloWorldClass HelloWorld
MembersMembers
Method SayHelloMethod SayHello
StatementsStatements
ExpressionExpression
SystemSystem
CodeDOM graphCodeDOM graph
AssemblyAssembly
compilingcompilingCodeDOM graphCodeDOM graph
compilingcompilingsource codesource code
parsingparsingsource codesource code
generatinggeneratingsource codesource code
Source code generation
MotivationsAvoiding repetitive coding tasks
Generation of typed collections
Pattern based code generationTie pattern implementation
Implementation of language independent code wizards and tools
Designer support
HelloWorld CodeDom graph
namespace Samples{ using System; public class Hello { public void SayHello() { Console.WriteLine("Hello World!"); } }}
CompileUnitCompileUnit
NamespacesNamespaces
Namespace Sample Namespace Sample
ImportsImports TypesTypes
Class HelloWorldClass HelloWorld
MembersMembers
Method SayHelloMethod SayHello
StatementsStatements
ExpressionExpression
SystemSystem
CodeDom elements (1/3)
CodeObjectCommon base class for most CodeDOM objects
CodeCompileUnitContainer for a CodeDOM program graph
CodeCommentRepresents a comment
CodeExpressionBase class for other code expression objects:
CodeArrayCreateExpression
CodeCastExpression
CodeMethodInvokeExpression
…
CodeDom elements (2/3)
CodeNamespaceRepresents a namespace declaration
CodeNamespaceImportRepresents a namespace import directive
CodeStatementBase class for other code statement objects:
CodeAssignStatement
CodeMethodReturnStatement
CodeIterationStatement
…
CodeDom elements (3/3)
CodeTypeMemberBase class for a member of a type:
CodeMemberField
CodeMemberMethodCodeConstructor
CodeTypeConstructor (static ctor)
CodeEntryPointMethod
CodeMemberProperty
…
CodeTypeReferenceRepresents a reference to a type
ICodeGenerator (1/3)
Generation sourcesCodeCompileUnit
CodeExpression
CodeNamespace
CodeStatement
CodeTypeDeclaration
Generation inputsCodeDOM element
TextWriter
Generation options
ICodeGenerator (2/3)
Generation support verificationAre arrays of arrays supported?
Can I nest multiple types?
Are goto statements supported?
…
CodeDomProvider provider = new CodeDomProvider provider = new Microsoft.CSharp.CSharpCodeProvider();Microsoft.CSharp.CSharpCodeProvider(); ICodeGenerator generator = provider.CreateGenerator();ICodeGenerator generator = provider.CreateGenerator(); if (generator.Supports(GeneratorSupport.NestedTypes) == false)if (generator.Supports(GeneratorSupport.NestedTypes) == false) {{ //we have a problem...//we have a problem... }}
ICodeGenerator (3/3)
Generation optionsBlank lines between members
Bracing style (“Block” or “C”)
Else on closing
Indent string
CodeGeneratorOptions options = new CodeGeneratorOptions(); options.BlankLinesBetweenMembers = true; options.BracingStyle = "C"; // options.BracingStyle = “Block"; options.ElseOnClosing = true; options.IndentString = " ";
Generating source Generating source codecode
demodemo
Agenda
CodeDOMIntroduction
Assembly compilation
Source code generation
Source code parsing
Advanced conceptsTemplate based source code generation
On-the-fly proxy generation
Source code parsing
public static void Main(String[] args ){ String usr; FileStream f; StreamWriter w; try { usr=Environment.GetEnvironmentVariable("USERNAME"); f=new FileStream(“C:\\test.txt",FileMode.Create); w=new StreamWriter(f); w.WriteLine(usr); w.Close(); } catch (Exception e){ Console.WriteLine("Exception:"+e.ToString()); }}
public static void Main(String[] args ){ String usr; FileStream f; StreamWriter w; try { usr=Environment.GetEnvironmentVariable("USERNAME"); f=new FileStream(“C:\\test.txt",FileMode.Create); w=new StreamWriter(f); w.WriteLine(usr); w.Close(); } catch (Exception e){ Console.WriteLine("Exception:"+e.ToString()); }}
Source codeSource code
CompileUnitCompileUnit
NamespacesNamespaces
Namespace Sample Namespace Sample
ImportsImports TypesTypes
Class HelloWorldClass HelloWorld
MembersMembers
Method SayHelloMethod SayHello
StatementsStatements
ExpressionExpression
SystemSystem
CodeDOM graphCodeDOM graph
AssemblyAssembly
compilingcompilingCodeDOM graphCodeDOM graph
compilingcompilingsource codesource code
parsingparsingsource codesource code
generatinggeneratingsource codesource code
Source code parsing
MotivationsEnabling iterative designer/modeling support
Source code migrationPorting source code from one language into another
There is only one problem…
CodeDomProvider csp = new Microsoft.CSharp.CSharpCodeProvider(); CodeDomProvider vbp = new Microsoft.VisualBasic.VBCodeProvider();
ICodeParser parser = csp.CreateParser(); ICodeGenerator generator = vbp.CreateGenerator();
CodeCompileUnit cu = parser.Parse(new StreamReader("Test.cs"));
generator.GenerateCodeFromCompileUnit(cu, new StreamWriter("Test.vb"),
new CodeGeneratorOptions());
…and no real solution to it
Neither VB.NET, C# nor J# CodeDomProviders implement ICodeParser
Managed C++ implements ICodeParser, but it works only in the VS.NET designer scenario
Managed C++ implements a method based parser
Returns a CodeStatementCollection
Can’t be used for whole source files
Agenda
CodeDOMIntroduction
Assembly compilation
Source code generation
Source code parsing
Advanced conceptsTemplate based source code generation
On-the-fly proxy generation
public class Listpublic class List{{ private object[] elements;private object[] elements; private int count;private int count;
public void Add(object element) {public void Add(object element) { if (count == elements.Length) Resize(count * 2);if (count == elements.Length) Resize(count * 2); elements[count++] = element;elements[count++] = element; }}
public object this[int index] {public object this[int index] { get { return elements[index]; }get { return elements[index]; } set { elements[index] = value; }set { elements[index] = value; } }}
public int Count {public int Count { get { return count; }get { return count; } }}}}
Problem statement
List intList = new List();List intList = new List();
intList.Add(1); // Argument is boxedintList.Add(1); // Argument is boxedintList.Add(2); // Argument is boxedintList.Add(2); // Argument is boxedintList.Add("Three"); // Should be an errorintList.Add("Three"); // Should be an error
int i = (int)intList[0]; // Cast requiredint i = (int)intList[0]; // Cast required
A possible solution
public class public class IntListIntList{{ private private intint[] elements;[] elements; private int count;private int count;
public void Add(public void Add(intint element) { element) { if (count == elements.Length) Resize(count * 2);if (count == elements.Length) Resize(count * 2); elements[count++] = element;elements[count++] = element; }}
public public intint this[int index] { this[int index] { get { return elements[index]; }get { return elements[index]; } set { elements[index] = value; }set { elements[index] = value; } }}
public int Count {public int Count { get { return count; }get { return count; } }}}}
IntList intList = new IntList();IntList intList = new IntList();
intList.Add(1); // No boxingintList.Add(1); // No boxingintList.Add(2); // No boxingintList.Add(2); // No boxingintList.Add("Three"); // Compile-time errorintList.Add("Three"); // Compile-time error
int i = intList[0]; // No cast requiredint i = intList[0]; // No cast required
Template based source code generation
MotivationsAvoiding the generic approach
Downcasting/boxing is expensive
Type safety can’t be guaranteed
Base class can’t be chosen for a given implementation
class A : Immutableclass A : Immutableclass A : Volatileclass A : Volatile
All this (and more) will be addressed All this (and more) will be addressed through .NET Generics as well!through .NET Generics as well!
Generating parameterized types
CompileUnitCompileUnit
NamespacesNamespaces
Namespace Sample Namespace Sample
ImportsImports TypesTypes
Class HelloWorldClass HelloWorld
MembersMembers
Method SayHelloMethod SayHello
StatementsStatements
ExpressionExpression
SystemSystem
CollectionCollectiongraph graph
public static void Main(String[] args ){ String usr; FileStream f; StreamWriter w; try { usr=Environment.GetEnvironmentVariable("USERNAME"); f=new FileStream(“C:\\test.txt",FileMode.Create); w=new StreamWriter(f); w.WriteLine(usr); w.Close(); } catch (Exception e){ Console.WriteLine("Exception:"+e.ToString()); }}
public static void Main(String[] args ){ String usr; FileStream f; StreamWriter w; try { usr=Environment.GetEnvironmentVariable("USERNAME"); f=new FileStream(“C:\\test.txt",FileMode.Create); w=new StreamWriter(f); w.WriteLine(usr); w.Close(); } catch (Exception e){ Console.WriteLine("Exception:"+e.ToString()); }}
StringCollection StringCollection
public static void Main(String[] args ){ String usr; FileStream f; StreamWriter w; try { usr=Environment.GetEnvironmentVariable("USERNAME"); f=new FileStream(“C:\\test.txt",FileMode.Create); w=new StreamWriter(f); w.WriteLine(usr); w.Close(); } catch (Exception e){ Console.WriteLine("Exception:"+e.ToString()); }}
public static void Main(String[] args ){ String usr; FileStream f; StreamWriter w; try { usr=Environment.GetEnvironmentVariable("USERNAME"); f=new FileStream(“C:\\test.txt",FileMode.Create); w=new StreamWriter(f); w.WriteLine(usr); w.Close(); } catch (Exception e){ Console.WriteLine("Exception:"+e.ToString()); }}
Int32CollectionInt32Collection
public static void Main(String[] args ){ String usr; FileStream f; StreamWriter w; try { usr=Environment.GetEnvironmentVariable("USERNAME"); f=new FileStream(“C:\\test.txt",FileMode.Create); w=new StreamWriter(f); w.WriteLine(usr); w.Close(); } catch (Exception e){ Console.WriteLine("Exception:"+e.ToString()); }}
public static void Main(String[] args ){ String usr; FileStream f; StreamWriter w; try { usr=Environment.GetEnvironmentVariable("USERNAME"); f=new FileStream(“C:\\test.txt",FileMode.Create); w=new StreamWriter(f); w.WriteLine(usr); w.Close(); } catch (Exception e){ Console.WriteLine("Exception:"+e.ToString()); }}
FooCollection FooCollection
System.StringSystem.String
System.Int32System.Int32
Nonsense.FooNonsense.Foo
Implementation tips
Parameterize the creation of the CodeDOM graph
Types
Type names
Namespace names
CodeTypeDeclaration cs = new CodeTypeDeclaration(typeName);
CodeMemberField count = new CodeMemberField(type, "count");
CodeNamespace ns = new CodeNamespace(namespaceName);
Template based source Template based source code generationcode generation
demodemo
Agenda
CodeDOMIntroduction
Assembly compilation
Source code generation
Source code parsing
Advanced conceptsTemplate based source code generation
On-the-fly proxy generation
On-the-fly proxy generation
MotivationsMethod call interception
Ensure thread safety
Logging/tracing
Tie-generationMaking an interface aware of the implementation context
Generating proxies
TypeType(Assembly(Assembly)) ProxyProxy
AssemblyAssembly
public static void Main(String[] args ){ String usr; FileStream f; StreamWriter w; try { usr=Environment.GetEnvironmentVariable("USERNAME"); f=new FileStream(“C:\\test.txt",FileMode.Create); w=new StreamWriter(f); w.WriteLine(usr); w.Close(); } catch (Exception e){ Console.WriteLine("Exception:"+e.ToString()); }}
public static void Main(String[] args ){ String usr; FileStream f; StreamWriter w; try { usr=Environment.GetEnvironmentVariable("USERNAME"); f=new FileStream(“C:\\test.txt",FileMode.Create); w=new StreamWriter(f); w.WriteLine(usr); w.Close(); } catch (Exception e){ Console.WriteLine("Exception:"+e.ToString()); }}
Source Source
CompileUnitCompileUnitNamespacesNamespaces
Namespace Sample Namespace Sample ImportsImports TypesTypes
Class HelloWorldClass HelloWorldMembersMembersMethod SayHelloMethod SayHello
StatementsStatementsExpressionExpression
SystemSystem
Proxy Proxy graphgraph
ReflectionReflection
InjectionInjection GenerationGeneration
CompilationCompilation
CompileUnitCompileUnitNamespacesNamespaces
Namespace Sample Namespace Sample ImportsImports TypesTypes
Class HelloWorldClass HelloWorldMembersMembersMethod SayHelloMethod SayHello
StatementsStatementsExpressionExpression
SystemSystem
ReflectedReflected graph graph
Implementation tips (1/5)
Only type contracts are reflectableNo managed access to implementation
Intercept interfaces not classesSimplifies programming model
Generate constructors that initialize the underlying instance Be aware of unsupported constructs
Unsafe (string’s char*)Nested namespaces
Use IsSpecialName to detect property and event methods
Implementation tips (2/5)
Interface interception
public Interface IFoo{ int foo(int i);}
public class A : B, IFoo{ public A(string name){;} public int foo(int i){;} public int doIt(){;}}
public class Proxy : C, IFoo{ private A __tieObject; public Proxy(string name) { // pre ctor injection __tieObj = new A(name); // post ctor injection } public int foo(int i) { // pre method call injection int __returnValue; __returnValue = __tieObj.foo(i); // post method call injection return __returnValue; }}
Implementation tips (3/5)
Delegates and events allow flexible code injection
public delegate void MethodCallInjection(CodeMemberMethod mm, MethodInfo mi);
public delegate void CtorCallInjection(CodeConstructor cc, ConstructorInfo ci);
public delegate void PropertyCallInjection(CodeMemberProperty mp, PropertyInfo pi);
public event MethodCallInjection OnPreMethodCallInjection;public event MethodCallInjection OnMethodCallInjection;public event MethodCallInjection OnPostMethodCallInjection;
public event CtorCallInjection OnPreCtorCallInjection;…
Implementation tips (4/5)
Injection delegate implementationpublic void ParamCheckInjection(CodeMemberMethod mm, MethodInfo mi){ foreach (ParameterInfo pi in mi.GetParameters()) { mm.Statements.Add( new CodeExpressionStatement ( new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeTypeReferenceExpression("System.Console"),
"WriteLine”), new CodePrimitiveExpression(pi.Name + ": {0}"), new CodeMethodInvokeExpression(new CodeVariableReferenceExpression(pi.Name), "ToString")))); }} XmlNodeList SelectNodes(string xpath, XmlNamespaceManager nsmgr)
{ System.Console.WriteLine("xpath: {0}", xpath.ToString()); System.Console.WriteLine("nsmgr: {0}", nsmgr.ToString());
Implementation tips (5/5)
Creating an injected instance
public object CreateInstance(string typeName, params object[] args){ Type[] types = new Type[args.Length]; for (int i=0; i < args.Length; i++) { types[i] = args[i].GetType(); }
Type injectedType = compiledAssembly.GetType(typeName, true); ConstructorInfo ci = injectedType.GetConstructor(types); return ci.Invoke(args);}
IFoo iProxy = (IFoo) xxx.CreateInstance(“Proxy”, “Hello”);int s = iProxy.Foo(2);
On-the-fly proxy On-the-fly proxy generationgeneration
demodemo
Agenda
CodeDOMIntroduction
Assembly compilation
Source code generation
Source code parsing
Advanced conceptsTemplate based source code generation
On-the-fly proxy generation
Summary
Summary
Assembly compilationOn-the-fly code generation and compilation
Source code generationAvoiding repetitive coding tasksPattern based code generationImplementation of language independent code wizards and tools
Source code parsingEnabling iterative designer/modeling supportSource code migration
Ask The ExpertsGet Your Questions Answered
I will be available in the ATE area after this session
Community Resources
Community Resourceshttp://www.microsoft.com/communities/default.mspx
Most Valuable Professional (MVP)http://www.mvp.support.microsoft.com/
NewsgroupsConverse online with Microsoft Newsgroups, including Worldwidehttp://www.microsoft.com/communities/newsgroups/default.mspx
User GroupsMeet and learn with your peershttp://www.microsoft.com/communities/usergroups/default.mspx
evaluationsevaluations
© 2003 Microsoft Corporation. All rights reserved.© 2003 Microsoft Corporation. All rights reserved.This presentation is for informational purposes only. MICROSOFT MAKES NO WARRANTIES, EXPRESS OR IMPLIED, IN THIS SUMMARY.This presentation is for informational purposes only. MICROSOFT MAKES NO WARRANTIES, EXPRESS OR IMPLIED, IN THIS SUMMARY.