Click here to Skip to main content
15,867,704 members
Articles / Desktop Programming / WPF

Roslyn based Simulated Multiple Inheritance Usage Patterns (Part 2)

Rate me:
Please Sign up or sign in to vote.
4.93/5 (5 votes)
4 Jan 2015CPOL12 min read 26.2K   404   9   2
Continue describing usage of NP.WrapperGenerator.vsix visual studio extension. Present more complex cases of multiple inheritance including diamond multiple inheritance.

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:

Image 1

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) // unselect old item
            {
                _selectedItem.IsItemSelected = false;
            }

            _selectedItem = value;

            if (_selectedItem != null) // select the new item
            {
                _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:

XAML
<local:MyTestSelectablePersonCollection x:Key="TheTestSelectablePersonCollection" />
<local:MyTestSelectableCarCollection x:Key="TheTestSelectableCarCollection" />      

The rows of buttons are defined as ItemControls at the bottom of the file:

XAML
<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:

XAML
<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>
            <!-- Call ToggleSelection method when when MouseDown event 
                 is fired on the item -->
            <i:EventTrigger EventName="MouseDown">
                <ei:CallMethodAction MethodName="ToggleIsSelected"
                                     TargetObject="{Binding Path=DataContext, ElementName=ItemPanel}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </Grid>
    <DataTemplate.Triggers>
        <!-- make TheOpacityBorder less opaque when the mouse is over-->
        <Trigger Property="IsMouseOver"
                 Value="True">
            <Setter TargetName="TheOpacityBorder"
                    Property="Opacity"
                    Value="0.3" />
        </Trigger>
        <DataTrigger Binding="{Binding Path=IsItemSelected}"
                     Value="True">
            <!-- make TheOpacityBorder completely transparent when the 
                 item is selected-->
            <Setter TargetName="TheOpacityBorder"
                    Property="Opacity"
                    Value="0" />

            <!-- make the ItemPanel non-responsive to the event when
                 the item is selected (this is to prevent unselection
                 when clicking on the same item again) -->
            <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:

XAML
<DataTrigger Binding="{Binding Path=IsItemSelected}"
             Value="True">
    <!-- make TheOpacityBorder completely transparent when the 
         item is selected-->
    <Setter TargetName="TheOpacityBorder"
            Property="Opacity"
            Value="0" />

    <!-- make the ItemPanel non-responsive to the event when
         the item is selected (this is to prevent unselection
         when clicking on the same item again) -->
    <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:

XAML
<i:Interaction.Triggers>
    <!-- Call ToggleSelection method when when MouseDown event 
         is fired on the item -->
    <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:

XAML
<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 Persons row and top CarDataTemplate for the Cars row:

XAML
<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:

Image 2

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:

XAML
<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.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 };
    }
}  

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):

Image 3

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

Image 4

Diamond inheritance happens when two classes inherit from one class and then a 4th class inherits from these two classes:

Image 5

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:

Image 6

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)
    {
        // shared Selectable object
        Selectable selectable = new Selectable();

        // pass the same Selectable to SelectableMan
        // 'super-class'
        this.TheSelectableMan = new SelectableMan
        {
            TheMan = new Man
            {
                Name = name,
                IsWalking = isWalking,
            },
            TheSelectable = selectable
        };

        // and pass the same Selectable to 
        // SelectableBat 'super-class'
        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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect AWebPros
United States United States
I am a software architect and a developer with great passion for new engineering solutions and finding and applying design patterns.

I am passionate about learning new ways of building software and sharing my knowledge with others.

I worked with many various languages including C#, Java and C++.

I fell in love with WPF (and later Silverlight) at first sight. After Microsoft killed Silverlight, I was distraught until I found Avalonia - a great multiplatform package for building UI on Windows, Linux, Mac as well as within browsers (using WASM) and for mobile platforms.

I have my Ph.D. from RPI.

here is my linkedin profile

Comments and Discussions

 
QuestionSelectable Bat-woman Pin
Marc Koutzarov29-Dec-14 14:57
professionalMarc Koutzarov29-Dec-14 14:57 
AnswerRe: Selectable Bat-woman Pin
Nick Polyak29-Dec-14 15:40
mvaNick Polyak29-Dec-14 15:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.