Source Code
For some reason, Codeproject site does not allow me to upload the source code. So, you can download the source code from my own web site at WrapperGenerationCode.zip.
Important Note
I would really appreciate if you leave me a comment stating what you think about this article and how it can be improved. Thanks.
Introduction
The first language I started programming was C++. Later I switched first to Java and later to C#. One and, perhaps the only, thing that I missed from C++ was multiple class inheritance. I know that multiple class inheritance is thought to be too complex and prone to errors by many people. I have a different viewpoint, however. I think that first of all, multiple class inheritance was never implemented the way it should have been in C++. Secondly, even its C++ implementation is very valuable and leads to increased code reuse and separation of concerns if applied correctly. Hopefully, reading this article through will convince you of the same.
Article Simulated Multiple Inheritance Pattern for C# talked about simulating multiple inheritance in C# by using containment instead of inheritance and creating wrappers around the methods of the contained objects. This is the approach we also adopt here, except that in our case, wrapper generation is going to be automatic - without almost any extra effort by the developer. More about this will be discussed further.
The new Roslyn functionality coming with VS 2015 creates enormous potential for analyzing the existing code and building various VS extensions. We use the Roslyn based single file generator to generate the wrapper properties, methods and events.
As was mentioned above, our multiple inheritance implementation is based on creating wrappers around objects contained within a class. Creating wrappers can also be employed for implementing other patterns, e.g., an adapter patter as will be shown below.
There are some limitations to the results and the code presented in this article. For example, we are not providing any solutions for sharing the similar super classes (or implementing virtual inheritance in C++ terms). Also, at this point, the code cannot handle generic 'super-classes' or generic methods and the error diagnostics virtually does not exist. These limitations I plan to address in the second installment of this article.
This article is organized in the following way:
- We go over the definition of Wrappers and show how they facilitate implementing various patterns.
- We provide examples of using the wrapper generation VS extension.
- We explain the VS extension code.
All the samples can be run using VS 2015 preview. I plan to port these samples to newer versions of VS once they are released.
I talk more about Multiple Inheritance simulation, including polymorphism and diamond inheritance in two subsequent articles: Roslyn based Simulated Multiple Inheritance Usage Patterns (Part 1) and Roslyn based Simulated Multiple Inheritance Usage Patterns (Part 2).
Wrappers and Patterns
In this section, I go over the definition and examples of Wrappers and their usage for implementing various patterns.
Wrapper Sample
A very simple example of a wrapper is located under WrapperSample
project. A class Person
is being wrapped within PersonWrapper
class. The reason for creating Wrappers will be demonstrated a little further in the article; the purpose of this project is simply to demonstrate what the Wrappers are.
Person
is a very simple class containing:
- an event -
NameChangedEvent
- a property -
Name
- and a method -
PrintInfoToConsole()
public class Person
{
public event Action<string> NameChangedEvent = null;
string _name;
public string Name
{
get
{
return _name;
}
set
{
if (_name == value)
return;
_name = value;
if (NameChangedEvent != null)
{
NameChangedEvent(_name);
}
}
}
public void PrintInfoToConsole()
{
Console.WriteLine("Name = " + Name);
}
}
Class PersonWrapper
demonstrates how each one of these class members (an event, a property and a method) should be wrapped:
public class PersonWrapper
{
Person _person;
public Person ThePerson
{
set
{
_person = value;
}
}
public event Action<string> NameChangedEvent
{
add
{
_person.NameChangedEvent += value;
}
remove
{
_person.NameChangedEvent -= value;
}
}
public string Name
{
get
{
return _person.Name;
}
set
{
_person.Name = value;
}
}
public void PrintInfoToConsole()
{
_person.PrintInfoToConsole();
}
public static implicit operator Person(PersonWrapper personWrapper)
{
return personWrapper._person;
}
}
As you can see above, on top of the wrappers for the event, the property and the method, the PersonWrapper
class also has _person
object, the Person
setter property and an implicit conversion operator.
Person
property and _person
object represent the wrapped object whose members are actually called. The conversion operator converts the wrapper object into the wrapped object.
Note that the Wrapper can change a name of a wrapped event, property or method: e.g., we could have named wrapper property PersonName
instead of Name
. Also note that the wrapper can also change the encapsulation level, e.g., the same property Name
could be made protected
within the Wrapper.
Using Wrappers to Create Adapters
One of the patterns described in the famous "Gang of 4" book "Design Patterns" is Adapter pattern, also called Wrapper.
The purpose of Adapter is to adapt an already existing implementation to an already existing interface.
Assume that you have an implementation of class Person
that was presented above. Assume that you have a number of methods written against an interface IHumanBeing
that has all the same members as Person
only differently named:
public interface IHumanBeing
{
event Action<string> HumanBeingNameChangedEvent;
string HumanBeingName { get; set; }
void PrintHumanBeingInfoToConsole();
}
Most of your classes and methods know how to deal with IHumanBeing
, interface but have no clue about Person
class. Assume also that you do not want or cannot modify the Person
code, i.e., you cannot change it to implement IHumanBeing
interface. This can be because a lot of your other code depends on Person
class or because you do not have the Person
's class code - only compiled DLL, or for any other reason.
In such case, you can adapt Person
class to IHumanBeing
interface. The Adapter
class will implement IHumanBeing
interface by wrapping the Person
's events, properties and methods:
public class PersonAdapter : IHumanBeing
{
Person _person;
public Person ThePerson
{
set
{
_person = value;
}
}
public string HumanBeingsName
{
get
{
return _person.Name;
}
set
{
_person.Name = value;
}
}
public event Action<string> HumanBeingsNameChangedEvent
{
add
{
_person.NameChangedEvent += value;
}
remove
{
_person.NameChangedEvent -= value;
}
}
public void PrintHumanBeingsInfoToConsole()
{
_person.PrintInfoToConsole();
}
}
Project AdapterSample
shows how such Adapter works.
Person
, IHumanBeing
and PersonAdapter
have been described above.
Main class Program
contains method DetectNameChange(IHumanBeing humanBeing)
that accepts IHumanBeing
argument, sets a handler for its HumanBeingNameChangedEvent
, then sets the HumanBeingName
and finally calls PrintHumanBeingsInfoToConsole()
method:
static void DetectNameChange(IHumanBeing humanBeing)
{
humanBeing.HumanBeingsNameChangedEvent += HumanBeing_HumanBeingsNameChangedEvent;
humanBeing.HumanBeingsName = "Nick";
humanBeing.PrintHumanBeingsInfoToConsole();
}
private static void HumanBeing_HumanBeingsNameChangedEvent(string name)
{
Console.WriteLine("New name is " + name + "\n");
}
DetectNameChange
method is called on a PersonAdapter
object from within Main
method:
static void Main(string[] args)
{
Person person = new Person();
PersonAdapter personAdapter = new PersonAdapter { ThePerson = person };
DetectNameChange(personAdapter);
}
After running the sample, you can see that all the functionality of Person
class is working when accessed through the adapter - the Name
is being assigned, the NameChangeEvent
fires and the PrintInfoToConsole()
method prints some information to console.
Speaking in general terms, the Adapter pattern changes the adapted class to satisfy a certain interface:
As you can see, our Wrapper paradigm (with renaming) proved to be useful for implementing the Adapter pattern.
Another very similar pattern that can be implemented using the same paradigm is Proxy pattern (from the same book). Only, in this case, the Adaptee
object can be nullable and a check for null
is required).
Imitating Multiple Class Inheritance with the Help of Wrappers
C++ class inheritance allows the subclass to inherit the public
and protected
fields and methods of its super-classes. On top of that, there are implicit conversions from the subtype to each of the super-types. In general, this can be easily mimicked with the Wrappers as our example demonstrates.
The Multiple Class Inheritance sample is located within MultipleClassInheritanceSample
project.
Within the sample, we 'derive' class SelectablePerson
from two classes: Person
and Selectable
Person
is almost the same as Person
of the previous examples, except that in order to simplify it, I removed from it the event.
Selectable
class is also very simple - it contains IsSelected
Boolean property, IsSelectedChangedEvent
that fired when IsSelected
changes and ToggleIsSelected()
method whose name explains what it does:
public class Selectable
{
public event Action<bool> IsSelectedChangedEvent = null;
bool _isSelected = false;
public bool IsSelected
{
get
{
return _isSelected;
}
set
{
if (_isSelected == value)
return;
_isSelected = value;
if (IsSelectedChangedEvent != null)
IsSelectedChangedEvent(_isSelected);
}
}
public void ToggleIsSelected()
{
IsSelected = !IsSelected;
}
}
SelectablePerson
class contains Wrappers around members of Person
and Selectable
classes as well as the corresponding implicit conversion operators:
public class SelectablePerson
{
Selectable _selectable = null;
public Selectable TheSelectable
{
set
{
_selectable = value;
}
}
Person _person = null;
public Person ThePerson
{
set
{
_person = value;
}
}
public event Action<bool> IsSelectedChangedEvent
{
add
{
_selectable.IsSelectedChangedEvent += value;
}
remove
{
_selectable.IsSelectedChangedEvent -= value;
}
}
public bool IsSelected
{
get
{
return _selectable.IsSelected;
}
set
{
_selectable.IsSelected = value;
}
}
public void ToggleIsSelected()
{
_selectable.ToggleIsSelected();
}
public static implicit operator Selectable(SelectablePerson selectablePerson)
{
return selectablePerson._selectable;
}
public string Name
{
get
{
return _person.Name;
}
set
{
_person.Name = value;
}
}
public void PrintInfoToConsole()
{
_person.PrintInfoToConsole();
}
public static implicit operator Person(SelectablePerson selectablePerson)
{
return selectablePerson._person;
}
}
SelectablePerson
class is tested in Program
class:
static void Main(string[] args)
{
SelectablePerson selectablePerson =
new SelectablePerson
{
ThePerson = new Person(),
TheSelectable = new Selectable()
};
selectablePerson.Name = "Nick";
selectablePerson.IsSelectedChangedEvent += SelectablePerson_IsSelectedChangedEvent;
selectablePerson.ToggleIsSelected();
selectablePerson.PrintInfoToConsole();
}
private static void SelectablePerson_IsSelectedChangedEvent(bool isSelected)
{
Console.WriteLine("IsSelected changed to " + isSelected);
}
We create SelectablePerson
object, set its name, event handler, then toggle IsSelected
property and make sure that the event handler fires. Finally, we print the Name
property of the object to console.
Limitations of Wrapper based Multiple Class Inheritance
The main limitation of the Multiple Class Inheritance implementation considered above is that this
reference within the super-classes does not refer to the whole object - it refers to the wrapped object. Everyone who uses this pattern needs to understand this very clearly.
Another limitation of the above implementation is that you cannot override a virtual
method in the sub-class while using this method in the super-class. And this is again related to the fact that this
reference within a super-class will be pointing to the wrapped super-class object and not to the Wrapper sub-class.
In spite of the above limitations, the Multiple Class Inheritance described above can be very useful especially when combined with using interface adapters.
VS Extension and its Usage
We showed how to create adapters and imitate Multiple Class Inheritance using Wrappers. However, writing a Wrapper is a tedious, error prone mechanical process. Using Roslyn
, I built a VS 2015 Preview extension that generates the wrapper code on the fly based on class Attributes that specify Wrapper parameters.
In order to be able to use this VS extension, double click on NP.WrapperGenerator.vsix file within VSIX folder. The projects that use this VS extension will also have to reference the NP.WrapperAttrs.dll located under DLLs folder. NP.WrapperAttrs.dll contains the definitions of the class Attributes that specify the Wrapper parameters.
AdapterGeneratedSample
and MultipleClassInheritanceGeneratedSample
contain examples of generated Wrappers.
Adapter Generated Sample
Let us take a look at AdapterGeneratedSample
project. All the classes of this project are identical to those of AdapterSample
aside from PersonAdapter
class. Here is the code for this class:
[WrapsAndChanges(typeof(Person), WrappedItemsKind.Event,
"NameChangedEvent", "HumanBeingsNameChangedEvent")]
[WrapsAndChanges(typeof(Person), WrappedItemsKind.Property, "Name", "HumanBeingsName")]
[WrapsAndChanges(typeof(Person), WrappedItemsKind.Method,
"PrintInfoToConsole", "PrintHumanBeingsInfoToConsole")]
public partial class PersonAdapter : IHumanBeing
{
}
You can see that the class is declared 'partial'. The class Attributes at the top specify the wrappers - what they wrap from which class and the name of the wrapper. In order for the code generation to take place, we need to set the "Custom Tool" property of the class to "WrapperFileCodeGenerator
":
Whenever the file is saved (e.g., by pressing Ctrl-S), the PersonAdapter.wrapper.cs file is generated. This file contain the wrappers for the class:
Here is the content of the generated wrapper file:
namespace AdapterGeneratedSample
{
public partial class PersonAdapter
{
private Person _person;
public static implicit operator Person (PersonAdapter objectToConvert)
{
return objectToConvert._person;
}
public event Action<string> HumanBeingsNameChangedEvent
{
add { _person.NameChangedEvent += value; }
remove { _person.NameChangedEvent -= value; }
}
public Person ThePerson
{
get
{
return _person;
}
set
{
_person = value;
}
}
public String HumanBeingsName
{
get
{
return _person.Name;
}
set
{
_person.Name = value;
}
}
public void PrintHumanBeingsInfoToConsole()
{
_person.PrintInfoToConsole();
}
}
}
Running this project produces exactly the same results as running AdapterSample
project.
This sample demonstrates how to use WrapsAndChangesAttribute
. Here are the explanations for the arguments of the attribute's constructor:
- The first argument is the type that we want to wrap.
- The second argument is the kind of a class member that we want to wrap (can be
WrappedItemsKind.Event
, WrappedItemsKind.Property
or WrappedItemsKind.Method
). - The third argument is the name of a class member to wrap.
- The fourth argument is the name of the wrapper class member. If this argument is not passed, it is assumed that the wrapper and wrapped members have the same name.
- The fifth argument (not demonstrated in the sample above) allows to change the encapsulation level of the wrapper. It is of
enum
type EncapsulationLevel
:
public enum EncapsulationLevel
{
Unknown = 0,
Public = 1,
Internal = 2,
Protected = 3,
ProtectedInternal = 4,
Private = 5
}
Unknown
means that the encapsulation level of the wrapper stays the same as that of the wrapped class member.
There is also another attribute - WrapsAttribute
that can be used. It is less powerful - but more compact: less powerful because it does not allow to change the name or encapsulation method of the wrapped class member. More compact - because you can specify several objects to be wrapped within the same attribute. Its first two constructor arguments are the same as those of WrapsAndChangesAttribute
. They can be followed by unlimited number of string
s specifying the names of the class members to wrap, e.g.:
[Wraps(typeof(Person), WrappedItemsKind.Property, "Property1", "Property2", "Property3")]
Multiple Class Inheritance Generated Sample
Now let us take a look at MultipleClassInheritanceGeneratedSample
project. The code within this project is virtually identical to that of MultipleClassInheritanceSample
project with the important exception of SelectablePerson
class. Here is how the new SelectablePerson
code looks:
[Wraps(typeof(Selectable), WrappedItemsKind.Event, "IsSelectedChangedEvent")]
[Wraps(typeof(Selectable), WrappedItemsKind.Property, "IsSelected")]
[Wraps(typeof(Selectable), WrappedItemsKind.Method, "ToggleIsSelected")]
[Wraps(typeof(Person), WrappedItemsKind.Property, "Name")]
[Wraps(typeof(Person), WrappedItemsKind.Method, "PrintInfoToConsole")]
public partial class SelectablePerson
{
}
15 lines instead of 87 lines.
Just like in PersonAdapter.cs file in AdapterGeneratedSample
project, the SelectablePerson.cs file should have its "Custom Tool" property set to "WrapperFileCodeGenerator
". This will result in generating the file SelectablePerson.wrapper.cs that will absorb most of the complexity.
Explaining the Wrapper Generator Code
The Wrapper generator code is located under NP.WrapperGenerator
solution. It consists of three projects:
NP.WrapperGenerator
- the main project that uses creates the NP.WrapperGenerator.vsix VS extension file. NP.WrapperAttrs
- the project that defines the Wrapper attributes and some enumeration that they use. The DLL created by this project is used both by the Wrapper generator code and also by the user projects that use the wrapper generator as was shown above. NP.DOMGenerator
is the project that contains most of the Wrapper generation code used by the main NP.WrapperGenerator
project.
Note that we are using Roslyn for analyzing the code - getting information about the wrappers to generate, while for code generation we are using the CodeDOM instead. The reason for this is that even though Roslyn code generation is more powerful, it is terribly messy and verbose - you have to write tonnes of code to achieve very simple results and CodeDOM proved to be quite adequate for the task.
What distinguishes VS 2015 (preview) from the previous version of Visual Studio is that Roslyn now is the primary engine behind the code analysis and compilation and Roslyn workspace object is now directly accessible as one of the services from IVsSingleFileGenerator
implementations.
This fact, however, (that Roslyn workspace is accessible from single file generators) was very well hidden (unintentionally of course) by Microsoft and so I give kudos to StackOverflow contributors Slaks and JoshVarty for helping me to figure out how to do it: How do you create a VS single file generator with Roslyn.
In order to start working with single file generators, I had to follow the steps specified at Roslyn to install Roslyn, VS 2015 Preview SDK and SDK Templates VSIX package.
Then I created NP.WrapperGenerator
solution and project as "Visual Studio Package" - for some reason, my attempt to use "VSIX Project" template was not successful.
Then I had to add Roslyn DLLs by going to "Package Manager Console" window within your VS 2015 preview and type "Install-Package Microsoft.CodeAnalysis -Pre
" (as specified at Roslyn).
I removed all the files that were created by default (aside from the source.extension.vsixmanifest file) and added WrapperFileCodeGenerator
class to the file of the same name:
[ComVisible(true)]
[Guid("52B316AA-1997-4c81-9969-83604C09EEB4")]
[
CodeGeneratorRegistration
(
typeof(WrapperFileCodeGenerator),
"C# Wrapper File Code Class Generator",
"{FAE04EC1-301F-11D3-BF4B-00C04F79EFBC}",
GeneratesDesignTimeSource = true
)
]
[ProvideObject(typeof(WrapperFileCodeGenerator))]
public class WrapperFileCodeGenerator : IVsSingleFileGenerator
{
...
}
After that, I added and removed references to several DLLs. Most importantly, I've added a reference to Microsoft.VisualStudio.LanguageServices.dll file without which I would not be able to get access to the Roslyn Workspace
. This was, probably, the trickiest part, because this DLL is not listed in your assemblies - you have to browse to this reference - it is located under "C:\Program Files\Microsoft Visual Studio 14.0\Common7\IDE\PrivateAssemblies\" folder. Again, kudos to Slaks for pointing this out.
After that, the reference to the Roslyn workspace can be obtained by the following code:
public VisualStudioWorkspace TheWorkspace { get; set; }
public WrapperFileCodeGenerator()
{
IComponentModel componentModel =
(IComponentModel)Microsoft.VisualStudio.Shell.Package.GetGlobalService
(typeof(SComponentModel));
TheWorkspace = componentModel.GetService<visualstudioworkspace>();
}
</visualstudioworkspace>
VS 2015 Extension functionality in combination with Roslyn will make sure that the Workspace
object stays up to date while the project's code changes.
Interface IVsSingleFileGenerator
implemented by our class requires two methods: DefaultExtension(out string pbstrDefaultExtension)
and Generate(...)
.
DefaultExtensions
method specifies the extension of the generated file (in our case it is ".wrapper.cs"):
public int DefaultExtension(out string pbstrDefaultExtension)
{
pbstrDefaultExtension = ".wrapper.cs";
return VSConstants.S_OK;
}
Generate
method allows to specify the byte content of the generated file:
public int Generate
(
string wszInputFilePath,
string bstrInputFileContents,
string wszDefaultNamespace,
IntPtr[] rgbOutputFileContents,
out uint pcbOutput,
IVsGeneratorProgress pGenerateProgress
)
{
byte[] codeBytes =
GenerateCodeBytes(wszInputFilePath, bstrInputFileContents, wszDefaultNamespace);
int outputLength = codeBytes.Length;
rgbOutputFileContents[0] = Marshal.AllocCoTaskMem(outputLength);
Marshal.Copy(codeBytes, 0, rgbOutputFileContents[0], outputLength);
pcbOutput = (uint)outputLength;
return VSConstants.S_OK;
}
As you can see, all the work forming the generated file's content is done within GenerateCodeBytes(...)
method called by Generate(...)
method:
protected byte[] GenerateCodeBytes
(string filePath, string inputFileContent, string namespaceName)
{
string generatedCode = "";
DocumentId docId =
TheWorkspace
.CurrentSolution
.GetDocumentIdsWithFilePath(filePath).FirstOrDefault();
if (docId == null)
goto returnLabel;
Project project = TheWorkspace.CurrentSolution.GetProject(docId.ProjectId);
if (project == null)
goto returnLabel;
Compilation compilation = project.GetCompilationAsync().Result;
if (compilation == null)
goto returnLabel;
Document doc = project.GetDocument(docId);
if (doc == null)
goto returnLabel;
SyntaxTree docSyntaxTree = doc.GetSyntaxTreeAsync().Result;
if (docSyntaxTree == null)
goto returnLabel;
SemanticModel semanticModel = compilation.GetSemanticModel(docSyntaxTree);
if (semanticModel == null)
goto returnLabel;
ClassDeclarationSyntax classNode =
docSyntaxTree.GetRoot()
.DescendantNodes()
.Where((node) => (node.CSharpKind() ==
SyntaxKind.ClassDeclaration)).FirstOrDefault() as ClassDeclarationSyntax;
if (classNode == null)
goto returnLabel;
INamedTypeSymbol classType =
semanticModel.GetDeclaredSymbol(classNode) as INamedTypeSymbol;
if (classType == null)
goto returnLabel;
DOMClassBuilder codeBuilder = new DOMClassBuilder();
codeBuilder.TheCompilation = compilation;
codeBuilder.TheTypeToExtend = classType;
codeBuilder.BuildAll();
generatedCode = codeBuilder.GetCode();
returnLabel:
byte[] bytes = Encoding.UTF8.GetBytes(generatedCode);
return bytes;
}
Note that the Roslyn's Compilation
object pulled out of the Project
object contains all the information that you can get using System.Reflection
functionality about the project without actually requiring to have a reference to the project's DLL.
Out of the Compilation
objects we pull INamedTypeSymbol
classType
corresponding to the class for which we want to generate the Wrapper part. Both compilation and the type are assigned to a DOMClassBuild
object which figures out the Wrapper attributes of the class and generates the code:
codeBuilder.TheCompilation = compilation;
codeBuilder.TheTypeToExtend = classType;
codeBuilder.BuildAll();
generatedCode = codeBuilder.GetCode();
DOMClassBuilding
is located under NP.DOMGenerator
project. This class figures out the Wrapper attributes and based on them creates WrapsAttrView
objects - they contain exactly the same information as the Wrapper attributes without the need to recreate the attribute objects themselves. Based on WrapsAttrView
objects, the DOMClassBuilder
calls extension methods of the static
DOMCodeGenerator
class to generate the actual code.
Conclusion
This article shows how to use and create the single file code generator using Roslyn VS 2015 Extension API in order to generate the wrappers of class members. This can be used for creating Adapters or simulating Multiple Class Inheritance.
The code presented here has several shortcomings:
- It does not treat classes with generic types well.
- It has no diagnostics.
- It does not show how to deal with virtual inheritance (inheritance when two super-classes derive from the same super-super-class).
I plan to address these shortcomings in the second part of this article.
History
- 7th December, 2014: Initial version