Important Note
Friends, I would appreciate if you leave me a comment describing what you liked or did not like about this article.
Introduction
Visual Studio 2015 preview came with the Roslyn based compiler and ability to use Roslyn for creating VS extensions. This opens up a slew of opportunities that had never existed before and this is my attempt to tap into it. Roslyn allows to analyze the .NET code without loading it - before that such capability existed only in proprietary solutions, e.g. reshaper.
In Implementing Adapter Pattern and Imitated Multiple Inheritance in C# using Roslyn based VS Extension I presented a way of imitating multiple inheritance in C# using Roslyn based single file generator. The method of simulating multiple inheritance was based on the one described in Simulated Multiple Inheritance Pattern for C# article except that the 'sub-class' wrappers were automatically generated using a visual studio extension presented in that article.
This article together with Roslyn based Simulated Multiple Inheritance Usage Patterns (Part 1) presents usage examples of the NP.WrapperGenerator.vsix VS extension. Its main purpose is to show how to achieve greater code re-use and separation of concerns with the help of this extension. I also try to respond to the readers concerns showing that the resulting constructs are very similar to Multiple Inheritance indeed and, with the help of interfaces we can even achieve the polymorphic Multiple Inheritance.
Unlike the first part of the article (Roslyn based Simulated Multiple Inheritance Usage Patterns (Part 1)) this second part deals with more interesting and complex cases.
This is the sequel to Part 1 article, so it is recommended that you read part 1 first.
If you want to find out more about how NP.WrapperGenerator.vsix extension has been built - please take a look at Simulated Multiple Inheritance Pattern for C# article.
At the end of the article we show how to deal with Diamond Multiple Inheritance - when two super-classes derive from the same class.
In order to run the samples and install the extension you need to have VS 2015 Preview installed.
Installing the Visual Studio Extension NP.WrapperGenerator.vsix
In order to be able to work through the samples below, you need to install NP.WrapperGenerator.vsix extension that comes within VSIS.zip file. All you need to do to install it - is to unzip the folder and double click the extension. After that you should restart your VS 2015 instances - in order for them to be able to use the new extension.
If you played with the samples of the previous article, you might have the previous version of this extensions installed. In that case, you need to uninstall it before installing the new version. In order to do it - go to "Tools->Extensions and Updates" menu item within any version instance of your VS 2015. Find WrapperGenerator extension (usually at the bottom), click on it and press "Uninstall" button.
Unique Item Selection Sample
This sample is located under UniqueItemSelectionSample solution. The ides for this example I borrowed from one of my previous WPF articles - View-View Model based WPF and XAML Implementational Patterns.
The sample shows how to build a non-visual collection of non-visual items that allow only one item to be selected at a time - when an item is selected - the item that had been selected before is deselected.
The only constraint on the items is that they should implement ISelectable
interface:
public interface ISelectable
{
bool IsItemSelected { get; set; }
event Action<iselectable> ItemSelectedChangedEvent;
}
</iselectable>
And the implementations of that interface should fire ItemSelectedChangedEvent
whenever IsItemSelected
property changes.
In WPF article all the selectable items were derived from Selectable
class. Here we show how to 'derive' our classes from Selectable
using the wrapper generation.
First of all try running the project. Here is what you are going to see:
There are two rows of different object top row has people and bottom row has cars.
The purpose of having two different rows is to show that totally different classes - Person
and Car
can be made ISelectable
using our wrapper 'inheritance' - without true C# inheritance.
When you click on one button it gets selected and previously selected button gets in the same row unselected.
Now, let us take a look at the code. Here is an implementation of Selectable
class:
public class Selectable : ISelectable, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropChanged(string propName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
public event Action<iselectable> ItemSelectedChangedEvent;
bool _isItemSelected = false;
public bool IsItemSelected
{
get
{
return _isItemSelected;
}
set
{
if (_isItemSelected == value)
return;
_isItemSelected = value;
if (ItemSelectedChangedEvent != null)
ItemSelectedChangedEvent(this);
OnPropChanged("IsItemSelected");
}
}
public void ToggleIsSelected()
{
IsItemSelected = !IsItemSelected;
}
}
</iselectable>
ItenSelectedChangedEvent
is wired to fire when IsItemSelected
property changes. The selectable object itself is passed to it as the only argument. Also, in order for WPF bindings to notice the change in IsItemSelected
property, we fire PropertyChanged
event using OnPropChanged(...)
method.
Class Person
has only one simple property - Name
:
public class Person
{
public string Name { get; set; }
}
Class Car
has two simple properties - Make
and Model
:
public class Car
{
public string Make { get; set; }
public string Model { get; set; }
}
Class SelectablePerson
derives (in the wrapper sense) from classes Selectable
and Person
:
[OneWayEventWraps(typeof(Selectable), "PropertyChanged", "sender")]
[Wraps(typeof(Selectable), WrappedItemsKind.Event, "ItemSelectedChangedEvent")]
[Wraps(typeof(Selectable), WrappedItemsKind.Property, "IsItemSelected")]
[Wraps(typeof(Selectable), WrappedItemsKind.Method, "ToggleIsSelected")]
[Wraps(typeof(Person), WrappedItemsKind.Property, "Name")]
public partial class SelectablePerson : ISelectable, INotifyPropertyChanged
{
public SelectablePerson(string name)
{
this.TheSelectable = new Selectable();
this.ThePerson = new Person { Name = name };
}
}
As was explained in Roslyn based Simulated Multiple Inheritance Usage Patterns (Part 1), the attributes specify the types and the members to wrap - e.g.
[Wraps(typeof(Selectable), WrappedItemsKind.Event, "ItemSelectedChangedEvent")]
wraps ItemSelectedChangedEvent
of the Selectable
class.
In fact the SelectablePerson.wrapper.cs
partial class definition is generated containing the wrappers. (For that to happen - you have to have "Custom Tool" property of SelectablePerson
class set to "WrapperFileCodeGenerator" string - as also explained in Part 1 of this article).
The fact that types Selectable
and Person
are mentioned within Wraps
attributes - leads to the corresponding properties TheSelectable
of type Selectable
and ThePerson
of type Person
generated within SelectablePerson.wrapper.cs file:
public Selectable TheSelectable
{
get
{
return _selectable;
}
set
{
if ((_selectable != null))
{
_selectable.PropertyChanged -= _selectable_PropertyChanged;
}
_selectable = value;
if ((_selectable != null))
{
_selectable.PropertyChanged += _selectable_PropertyChanged;
}
}
}
public Person ThePerson
{
get
{
return _person;
}
set
{
_person = value;
}
}
The setting of the handlers for PropertyChanged
events within TheSelectable
property setter will be explained below.
The Selectable
and Person
objects specified by TheSelectable
and ThePerson
properties now play the roles of the base class objects. All the wrappers are built around the members of these objects. For example here how ItemSelectedChangedEvent
wrapper is defined:
public event Action<iselectable> ItemSelectedChangedEvent
{
add { _selectable.ItemSelectedChangedEvent += value; }
remove { _selectable.ItemSelectedChangedEvent -= value; }
}
</iselectable>
Here is how we define ToggleIsSelected
method wrapper:
public void ToggleIsSelected()
{
_selectable.ToggleIsSelected();
}
And here is how we wrap the Name
property of the Person
class:
public String Name
{
get
{
return _person.Name;
}
set
{
_person.Name = value;
}
}
Now let me explain the code that adds PropertyChanged
event handler within TheSelectable
property definition. This code is coming from OneWayEventWraps
attributed:
[OneWayEventWraps(typeof(Selectable), "PropertyChanged", "sender")]
For a detailed explanation of One Way Event Wrapping with this
Reference substitution, please, look at Roslyn based Simulated Multiple Inheritance Usage Patterns (Part 1).
This attribute results in a special type of event wrapping - instead of defining the add
and remove
parts of the event - we add an event handler to the 'base' object's event:
if ((_selectable != null))
{
_selectable.PropertyChanged += _selectable_PropertyChanged;
}
Within the event handler - we call the identical event on the 'derived' object except that instead of one of the arguments to the event, we pass this
reference:
private void _selectable_PropertyChanged(Object sender, PropertyChangedEventArgs e)
{
if ((PropertyChanged != null))
{
PropertyChanged(this, e);
}
}
The argument to be replaced by this
reference - is defined by third argument of OneWayEventWraps
attribute - in our case it is "sender" and we replace argument "sender" - the first argument of PropertyChangedEventHandler
by this
.
This is needed, because otherwise, this
reference passed to PropertyChanged
will be that of the 'base' Person
object and not that of the derived SelectablePerson
, so the WPF binding will not notice the change in SelectablePerson
's IsItemSelected
property.
In the next example we will show that ItemSelectedChangedEvent
is also better to wrap using OneWayEventWraps
attribute.
In a very similar fashion, SelectableCar
is 'derived' from Selectable
and Car
classes:
[OneWayEventWraps(typeof(Selectable), "PropertyChanged", "sender")]
[Wraps(typeof(Selectable), WrappedItemsKind.Event, "ItemSelectedChangedEvent")]
[Wraps(typeof(Selectable), WrappedItemsKind.Property, "IsItemSelected")]
[Wraps(typeof(Selectable), WrappedItemsKind.Method, "ToggleIsSelected")]
[Wraps(typeof(Car), WrappedItemsKind.Property, "Make", "Model")]
public partial class SelectableCar : ISelectable, INotifyPropertyChanged
{
public SelectableCar(string make, string model)
{
this.TheSelectable = new Selectable();
this.TheCar = new Car { Make = make, Model = model};
}
}
Now, take a look at CollectionWithUniqueItemSelection<T>
class. This class is described in detail in View-View Model based WPF and XAML Implementational Patterns article. This collection defines the unique selected behavior on its items providing the required plumbing for selection and unselection.
CollectionWithUniqueItemSelection<T>
is derived from ObservableCollection<T>
where T
should be ISelectable
. CollectionWithUniqueItemSelection<T>
class also defines SelectedItem
property - it is null when no item is selected, otherwise it is set to the currently selected item.
The code within the class makes sure that SelectedItem
is always pointing to the only item whose IsItemSelected
property is set to true. Here is the full code for this class:
public class CollectionWithUniqueItemSelection<T> : ObservableCollection<T>
where T : class, ISelectable
{
T _selectedItem = null;
public T SelectedItem
{
get
{
return _selectedItem;
}
set
{
if (_selectedItem == value)
return;
if (_selectedItem != null)
{
_selectedItem.IsItemSelected = false;
}
_selectedItem = value;
if (_selectedItem != null)
{
_selectedItem.IsItemSelected = true;
}
OnPropertyChanged(new PropertyChangedEventArgs("SelectedItem"));
}
}
void Init()
{
ConnectItems(this);
this.CollectionChanged += CollectionWithUniqueItemSelection_CollectionChanged;
}
void CollectionWithUniqueItemSelection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
DisconnectItems(e.OldItems);
ConnectItems(e.NewItems);
}
void DisconnectItems(IEnumerable items)
{
if (items == null)
return;
foreach (T item in items)
{
item.ItemSelectedChangedEvent -= item_ItemSelectedChangedEvent;
}
}
void ConnectItems(IEnumerable items)
{
if (items == null)
return;
foreach (T item in items)
{
item.ItemSelectedChangedEvent += item_ItemSelectedChangedEvent;
}
}
void item_ItemSelectedChangedEvent(ISelectable item)
{
if (item.IsItemSelected)
{
this.SelectedItem = (T)item;
}
else
{
this.SelectedItem = null;
}
}
public CollectionWithUniqueItemSelection()
{
Init();
}
public CollectionWithUniqueItemSelection(IEnumerable<T> collection) : base(collection)
{
Init();
}
}
We build our View Models by deriving from CollectionWithUniqueItemSelection<T>
and populating them with SelectablePerson
and SelectableCar
objects correspondingly:
public class MyTestSelectablePersonCollection : CollectionWithUniqueItemSelection<iselectable>
{
public MyTestSelectablePersonCollection()
{
Add(new SelectablePerson("Joe Doe"));
Add(new SelectablePerson("Jane Dane"));
Add(new SelectablePerson("Jack Flack"));
Add(new SelectablePerson("Judi Moodi"));
}
}
</iselectable>
public class MyTestSelectableCarCollection : CollectionWithUniqueItemSelection<iselectable>
{
public MyTestSelectableCarCollection()
{
Add(new SelectableCar("Ford", "Mustang"));
Add(new SelectableCar("Chevy", "Cavalier"));
Add(new SelectableCar("Toyota", "Corolla"));
Add(new SelectableCar("Honda", "Civic"));
}
}
</iselectable>
Now take a look at MainWindow.xaml file. We defined our View Models at the top of its Resources
section:
<local:MyTestSelectablePersonCollection x:Key="TheTestSelectablePersonCollection" />
<local:MyTestSelectableCarCollection x:Key="TheTestSelectableCarCollection" />
The rows of buttons are defined as ItemControl
s at the bottom of the file:
<ItemsControl x:Name="PersonSelectionControl"
Style="{StaticResource SelectableItemsControlStyle}"
ItemsSource="{StaticResource TheTestSelectablePersonCollection}"
local:AttachedProps.ItemInternalDataTemplateProperty="{StaticResource PersonDataTemplate}" />
<ItemsControl x:Name="CarSelectionControl"
Style="{StaticResource SelectableItemsControlStyle}"
ItemsSource="{StaticResource TheTestSelectableCarCollection}"
local:AttachedProps.ItemInternalDataTemplateProperty="{StaticResource CarDataTemplate}"
Grid.Row="1"/>
The style SelectableItemControlStyle
defines the ItemTemplate
for the individual items using SelectableItemDataTemplate
resource:
<DataTemplate x:Key="SelectableItemDataTemplate">
<Grid Background="Transparent"
x:Name="ItemPanel">
<Border x:Name="TheItemBorder"
Background="Black"
BorderBrush="White"
BorderThickness="1">
<ContentControl Content="{Binding}"
ContentTemplate="{Binding Path=(local:AttachedProps.ItemInternalDataTemplate),
RelativeSource={RelativeSource AncestorType=ItemsControl}}" />
</Border>
<Border x:Name="TheOpacityBorder"
Background="White"
Opacity="0.5" />
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDown">
<ei:CallMethodAction MethodName="ToggleIsSelected"
TargetObject="{Binding Path=DataContext, ElementName=ItemPanel}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Grid>
<DataTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter TargetName="TheOpacityBorder"
Property="Opacity"
Value="0.3" />
</Trigger>
<DataTrigger Binding="{Binding Path=IsItemSelected}"
Value="True">
<Setter TargetName="TheOpacityBorder"
Property="Opacity"
Value="0" />
<Setter TargetName="ItemPanel"
Property="IsHitTestVisible"
Value="False" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
As you can see, the darkness/lightness of each 'button' is controlled based on whether IsItemSelected
property is true
or false
via the data triggers:
<DataTrigger Binding="{Binding Path=IsItemSelected}"
Value="True">
<Setter TargetName="TheOpacityBorder"
Property="Opacity"
Value="0" />
<Setter TargetName="ItemPanel"
Property="IsHitTestVisible"
Value="False" />
</DataTrigger>
Also using Microsoft Expression Blend SDK triggers we call ToggleIsSelected()
method on the View Model of the corresponding item whenever that item is clicked:
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDown">
<ei:CallMethodAction MethodName="ToggleIsSelected"
TargetObject="{Binding Path=DataContext, ElementName=ItemPanel}" />
</i:EventTrigger>
</i:Interaction.Triggers>
Also the content of each 'button' is determined by attached AttachedProps.ItemInternalDataTemplate
property:
<Border x:Name="TheItemBorder"
Background="Black"
BorderBrush="White"
BorderThickness="1">
<ContentControl Content="{Binding}"
ContentTemplate="{Binding Path=(local:AttachedProps.ItemInternalDataTemplate),
RelativeSource={RelativeSource AncestorType=ItemsControl}}" />
</Border>
This property is set to PersonDataTemplate
for the Person
s row and top CarDataTemplate
for the Car
s row:
<DataTemplate x:Key="PersonDataTemplate">
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="White"
Text="{Binding Path=Name}"
Margin="10, 5" />
</DataTemplate>
<DataTemplate x:Key="CarDataTemplate">
<StackPanel Orientation="Horizontal"
Margin="5">
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="White"
FontWeight="Bold"
Text="{Binding Path=Make}"
Margin="5, 0" />
<TextBlock HorizontalAlignment="Center"
VerticalAlignment="Center"
Foreground="Green"
Text="{Binding Path=Model}"
Margin="5, 0" />
</StackPanel>
</DataTemplate>
When you run the application, you can see that CollectionWithUniqueItemSelection
class correctly forces no more than one selected item in each row, even though neither SelectablePerson
no SelectableCar
classes are derived from Selectable
(in strict C# sense)! This means that Polymorphism works in wrapper based inheritance!
Unique Selection with Selected Item Display Sample
When you run SelectedItemDisplaySample
project and press some buttons, you will see the following:
On the right you'll see the name of the selected person.
The code for SelectedItemDisplaySample
is very similar to that of the previous sample (aside from the lack of Car-related classes).
There is a small addition at the bottom of MainWindow.xaml file - we are adding the functionality to display the selected person's name:
<Grid Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
DataContext="{Binding Source={StaticResource TheTestSelectablePersonCollection}}">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="Selected Person:"/>
<TextBlock Grid.Row="1"
Text="{Binding SelectedItem.Name}" />
</Grid>
As you can see, the TextBlock
at the bottom is bound to SelectedItem.Name
path of the CollectionWithUniqueItemSelection
.
Now if you look at SelectablePerson
class, you'll see one interesting change - ItemSelecteChangedEvent
is now defined using OneWayEventWraps
attribute and not Wraps
attribute as in the previous sample:
[OneWayEventWraps(typeof(Selectable), "PropertyChanged", "sender")]
[OneWayEventWraps(typeof(Selectable), "ItemSelectedChangedEvent", "obj")]
[Wraps(typeof(Selectable), WrappedItemsKind.Property, "IsItemSelected")]
[Wraps(typeof(Selectable), WrappedItemsKind.Method, "ToggleIsSelected")]
[Wraps(typeof(Person), WrappedItemsKind.Property, "Name")]
public partial class SelectablePerson : ISelectable, INotifyPropertyChanged
{
public SelectablePerson(string name)
{
this.TheSelectable = new Selectable();
this.ThePerson = new Person { Name = name };
}
}
In fact if you try to uncomment the Wraps
and comment out the OneWayEventWraps
line, the sample will stop working. The reasons are very similar to those explained about PropertyChanged
event above and also in Roslyn based Simulated Multiple Inheritance Usage Patterns (Part 1) article.
Without OneWayEventWraps
attribute, the ItemSelectedChangedEvent
will set the SelectedItem
to the Selectable
'base' object and not to the full SelectablePerson
object. Selectable
'base' object does not have Name
property so there will be nothing to display. OneWayEventWraps
attribute, however, forces the SelectedItem
property to be set to the correct SelectablePerson
object.
Selectable and Describable Items Sample
In this sample are going to show that we can 'inherit' from three classes instead of two and besides that all the 3 'parts' of the resulting class are working together properly.
To start the sample, please, open and run SelectableAndDescribableItemSample
project. Here is what you are going to see (after pressing some buttons):
Each Person
item now has a description displayed in the TextBox
on the right. Moreover, you can change the description, click on some other buttons, than come back to the same person and you'll see the description's change has been recorded indeed.
The code of this sample is very similar to that of the previous one. It has an extra class Descrabable
containing Description
property that fires PropertyChanged
event on change:
public class Describable : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
#region Description Property
private string _description;
public string Description
{
get
{
return this._description;
}
set
{
if (this._description == value)
{
return;
}
this._description = value;
this.OnPropertyChanged("Description");
}
}
#endregion Description Property
}
SelectableDesribablePerson
class uses OneWayEventWraps
attribute for ItemSelectedChangedEvent
of Selectable
and also for PropertyChanged
event of both Selectable
and Describable
'super-classes':
[OneWayEventWraps(typeof(Selectable), "ItemSelectedChangedEvent", "obj")]
[OneWayEventWraps(typeof(Selectable), "PropertyChanged", "sender")]
[OneWayEventWraps(typeof(Describable), "PropertyChanged", "sender")]
[Wraps(typeof(Selectable), WrappedItemsKind.Property, "IsItemSelected")]
[Wraps(typeof(Selectable), WrappedItemsKind.Method, "ToggleIsSelected")]
[Wraps(typeof(Describable), WrappedItemsKind.Property, "Description")]
[Wraps(typeof(Person), WrappedItemsKind.Property, "Name")]
public partial class SelectableDescribablePerson : ISelectable, INotifyPropertyChanged
{
public SelectableDescribablePerson(string name, string description = null)
{
this.TheSelectable = new Selectable();
this.TheDescribable = new Describable { Description = description };
this.ThePerson = new Person { Name = name };
}
}
Implementing Diamond Inheritance
Diamond inheritance happens when two classes inherit from one class and then a 4th class inherits from these two classes:
There are different reasons why we might want to derive SelectableBatman
class from SelectableBat
and SelectableMan
and not from Sectable
, Bat
and Man
instead.
The main reason is that we might want to preserve some plumbing between Selectable
and Bat
within SelectableBat
and between Selectable
and Man
within SelectableMan
.
Other reasons can involve encapsulation e.g. if Selectable
might be an internal class and we might not be able to access it for derivation or that in our universe or else Bat
and Man
objects might be always selectable and there is no need for non-selectable Bat
and Man
classes.
In C++ the diamond inheritance is achieved by the keyword virtual
. This keyword has to take place at an early stage of one of either SelectableBat
or SelectableMan
inheriting from Selectable
. This is quite inconvenient and totally unnecessary - you have to choose which class is going to rely on the implementation of the other class before you actually have the need to create SelectableBatman
class. Moreover if you inherit SelectableBat
virtually and SelectableMan
non-virtually what if there is a need in the future to create some other combinations where SelectableBat
will have to provide its own implementation of Selectable
and SelectableMan
vice versa?
We'll show how to use our wrapper inheritance to get around this problem.
The diamond inheritance sample is located under DiamondInheritanceSample
project.
Here is what you get when you run the project and press some buttons:
Now, take a look at the code.
Class Selectable
is exactly the same as that of the previous projects.
Class Bat
has only one simple property IsFlying
signifying whether the Bat
object is currently flying or not:
public class Bat
{
public bool IsFlying { get; set; }
}
One might notice that actually IsFlying
property should fire PropertyChanged
event when changes - in order to register some visual change, but we are trying to keep our sample as light as possible - so for our purposes it is not necessary.
Class Man
has two simple properties: Name
and IsWalking
- the former just specifies the person's name, the latter - specifies whether the person is currently walking or not:
public class Man
{
public string Name { get; set; }
public bool IsWalking { get; set; }
}
SelectableBat
is derived from Selectable
and Bat
:
[OneWayEventWraps(typeof(Selectable), "PropertyChanged", "sender")]
[Wraps(typeof(Selectable), WrappedItemsKind.Event, "ItemSelectedChangedEvent")]
[Wraps(typeof(Selectable), WrappedItemsKind.Property, "IsItemSelected")]
[Wraps(typeof(Selectable), WrappedItemsKind.Method, "ToggleIsSelected")]
[Wraps(typeof(Bat), WrappedItemsKind.Property, "IsFlying")]
public partial class SelectableBat
{
}
SelectableMan
is derived from Selectable
and Man
:
[OneWayEventWraps(typeof(Selectable), "PropertyChanged", "sender")]
[Wraps(typeof(Selectable), WrappedItemsKind.Event, "ItemSelectedChangedEvent")]
[Wraps(typeof(Selectable), WrappedItemsKind.Property, "IsItemSelected")]
[Wraps(typeof(Selectable), WrappedItemsKind.Method, "ToggleIsSelected")]
[Wraps(typeof(Man), WrappedItemsKind.Property, "IsWalking", "Name")]
public partial class SelectableMan
{
}
SelectableBatman
is derived from SelectableBat
and SelectableMan
and the sharing of Selectable
part is ensured within its constructor:
[OneWayEventWraps(typeof(SelectableMan), "PropertyChanged", "sender")]
[Wraps(typeof(SelectableMan), WrappedItemsKind.Event, "ItemSelectedChangedEvent")]
[Wraps(typeof(SelectableMan), WrappedItemsKind.Property, "IsItemSelected")]
[Wraps(typeof(SelectableMan), WrappedItemsKind.Method, "ToggleIsSelected")]
[Wraps(typeof(SelectableMan), WrappedItemsKind.Property, "IsWalking", "Name")]
[Wraps(typeof(SelectableBat), WrappedItemsKind.Property, "IsFlying")]
public partial class SelectableBatMan : ISelectable, INotifyPropertyChanged
{
public SelectableBatMan(string name, bool isWalking, bool isFlying)
{
Selectable selectable = new Selectable();
this.TheSelectableMan = new SelectableMan
{
TheMan = new Man
{
Name = name,
IsWalking = isWalking,
},
TheSelectable = selectable
};
this.TheSelectableBat = new SelectableBat
{
TheBat = new Bat { IsFlying = isFlying},
TheSelectable = selectable
};
}
}
As you can see - we create a single Selectable
object within the constructor and pass it to both SelectableMan
and SelectableBat
'base class' constructors.
This is all that's needed to ensure the sharing of the Selectable
part between SelectableBat
and SelectableMan
parts of SelectableBatman
!
Conclusion
We've shown how to use auto-generated wrapper based Multiple Inheritance in C#. We've also shown that polymorphism or wrapper based sub-classes can be preserved by employing the interfaces.
Finally we presented a sample showing how simple it is to achive the famous diamond inheritance by using the generated wrappers. In fact our solution is considerably better than that built into C++ - it does not impose and rigid limitation when the superclasses are being created.