Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Roslyn based Simulated Multiple Inheritance Usage Patterns (Part 1)

0.00/5 (No votes)
29 Dec 2014 1  
describe how to use VS 2015 preview Roslyn base extension for simulating multiple inheritance in C# and provide usage examples

Important Note

Friends, I would appreciate if you leave me a comment describing what you liked or did not like about this article.

Introduction

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.

I had this idea of implementing Multiple Inheritance using code generation long ago but only Roslyn allowed its implementation. Before Roslyn there were no tools available for analysing the code and pulling all the information required for wrapper generation within the Visual Studio extensions - there were some proprietary solutions, of course, e.g. the resharper, but they were not avaible for everyone. With Roslyn, however, sky is the limit of what can be achieved. My wrapper generators might just be a tip of an iceberg of what will be coming.

Several commenters to my previous article were upset that I call the resulting pattern 'Imitated Multiple Inheritance'. They were arguing that the resulting constructs are not polymorphic.

One of the aims of this article is precisely to show how to achieve Multiple Inheritance Polymorphism using the generated code and with the help of the interfaces.

The main purpose of this article is to show how by using Roslyn generated Multiple Inheritance and Adapter patterns you can improve the separation of concerns and code reuse.

Roslyn integration with Visual Studio comes only with VS 2015 Preview - so this is what you need to use to run the article's samples.

Last time I tried to upload the whole code at ones and the codeproject did not allow me, so now. I splitted the source code into smaller chunks - project by project.

The capabilities of the Visual Studio Extension NP.WrapperGenerator.vsix have been expanded to include another type of event wrapping (I call it One Way Event Wrapping) which will be explained a little further in the article.

In this installment I'll give a review of the wrapper generation, discuss implementing polymorphic 'multiple inheritance' using interface, and describe One Way Event Wrapping.

In the next installment of this article I plan to provide some more striking WPF based samples of polymorphic multiple inheritance and also to provide a sample of (in)famous diamond inheritance implemented in C++ by using virtual keyword.

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.

Refresher

Here I give just a brief refresher of the previous article Implementing Adapter Pattern and Imitated Multiple Inheritance in C# using Roslyn based VS Extension in order for people to be able to read this article independently.

The Main Principles of Wrapper Generation with Inheritance Analogies

As was described in the previous article - the NP.WrapperGenerator.vsix extension can be used for generating a partial class file containing the wrappers around properties, events and methods of some other classes. We use class attributes to specify which wrappers should be generated and the "Custom Tool" property of the file that contains those attributes should be set to "WrapperFileCodeGenerator".

The class attributes allow also changing the names and encapsulation level of the wrapped class members, e.g. public property Name can be wrapped as protected property PersonName.

Wrapper Generation Sample

The purpose of this sample is to demonstrate the capabilities of NP.WrapperGenerator.vsix extension.

The sample solution is located under WrapperGenerationSample folder.

The class to be wrapped is called Person. It is a very simple class - containing two properties: Name and Age, one event NameChangedEvent and one method - void ChangeName(string newName):

public class Person
{
    string _name;
    public string Name
    {
        get
        {
            return _name;
        }

        set
        {
            _name = value;

            if (NameChangedEvent != null)
                NameChangedEvent(this);
        }
    }

    public int Age { get; set; }

    public event Action<person> NameChangedEvent = null;

    public void ChangeName(string newName)
    {
        Name = newName;
    }
}  
</person>

Class that wraps Person is called PersonWrapper. It is defined a partial, and it has a number of special class attributes that specify the wrapping:

[Wraps(typeof(Person), WrappedItemsKind.Property, "Name", "Age")]
[Wraps(typeof(Person), WrappedItemsKind.Event, "NameChangedEvent")]
[WrapsAndChanges(typeof(Person), WrappedItemsKind.Method, "ChangeName", "ChangePersonName", EncapsulationLevel.Internal)]
public partial class PersonWrapper
{
}  

In order for wrapper generation to take place, "Custom Tool" of PersonWrapper.cs file should be eset to "WrapperFileCodeGenerator":

Once the "Custom Tool" property is set, or once PersonWrapper.cs file modified and saved, the NP.WrapperGenerator extension will produce a dependent file PersonWrapper.wrapper.cs containing another part of this partial class with the wrappers:

Here is the content of PersonWrapper.wrapper.cs file:

using System;
using WrapperGenerationSample;


namespace WrapperGenerationSample
{
    
    
    public partial class PersonWrapper
    {
        
        private Person _person;
        
        public static implicit operator Person (PersonWrapper objectToConvert)
                                   {
                                       return objectToConvert._person;
                                   }
                         
        public event Action<person> NameChangedEvent
            { 
                add { _person.NameChangedEvent += value; } 
                remove { _person.NameChangedEvent -= value; } 
            }
        
        public Person ThePerson
        {
            get
            {
                return _person;
            }
            set
            {
                _person = value;
            }
        }
        
        public String Name
        {
            get
            {
                return _person.Name;
            }
            set
            {
                _person.Name = value;
            }
        }
        
        public Int32 Age
        {
            get
            {
                return _person.Age;
            }
            set
            {
                _person.Age = value;
            }
        }
        
        internal void ChangePersonName(String newName)
        {
            _person.ChangeName(newName);
        }
    }
}  
</person>

First of all it contains a public property ThePerson of type Person:

private Person _person;
...
public Person ThePerson
{
    get
    {
        return _person;
    }
    set
    {
        _person = value;
    }
}  

This property represents an object to wrap. The ThePerson object can be set in the constructor of the PersonWrapper class - using the languate of inheritance, ThePerson object represents the base class of the PersonWrapper and its setting it within the PersonWrapper's constructor maps into calling base(...) constructor of the base class.

The rest of the functionality of PersonWrapper.wrapper.cs class simply contains wrappers around ThePerson object's properties, events and methods with an important exception of the implicit converter operator:

public static implicit operator Person (PersonWrapper objectToConvert)
                           {
                               return objectToConvert._person;
                           }  

It converts PersonWrapper object into Person by simply returning the object referenced by ThePerson property. This maps into implicit conversion of a sub-class into a super class.

Now, take a look at the class attributes above PersonWrapper class. (These attrubtes, BTW require a reference to NP.WrapperAttrs.dll file located within WrapperGenerationSample/Dlls folder).

Wraps attribute requires the type of the wrapped class (the superclass), the kind of class member being wrappers - property, event or method and a list of names of class members of that kind to be wrapped e.g. in our case it can be "Name" and "Age" properties or "NameChangedEvent" event:

[Wraps(typeof(Person), WrappedItemsKind.Property, "Name", "Age")]
[Wraps(typeof(Person), WrappedItemsKind.Event, "NameChangedEvent")]  

WrapsAndChanges attribute is more powerful - it allows to change the name and encapsulation level of the class member, but it only deals with one class member at a time - you cannot pass several members to it at once as we did above with "Name" and "Age":

[WrapsAndChanges(typeof(Person), WrappedItemsKind.Method, "ChangeName", "ChangePersonName", EncapsulationLevel.Internal)]  

This attribute changes the name of ChangeName(...) method to ChangePersonName(...) and sets the wrapper's methods encapsulation level to internal. BTW, if the encapsulation level is not specified it is going to be the same as that of the wrapped class member - not necessarily public.

Example of usage of the PersonWrapper class can be found within Program.cs file:

static void Main(string[] args)
{
    PersonWrapper personWrapper = new PersonWrapper
    {
        // within the constructor set the 'base' object
        ThePerson = new Person { Name = "John", Age = 30 }
    };

    // set the event handler
    personWrapper.NameChangedEvent += PersonWrapper_NameChangedEvent;

    // change the name and make sure that the handler prints the new name
    personWrapper.ChangePersonName("Johnathan");
}

private static void PersonWrapper_NameChangedEvent(Person obj)
{
    Console.WriteLine(obj.Name);
}  

Discussion About Inheritance and Multiple Inheritance

Some commentators to my previous article thought that the wrapper generation presented above has nothing to do with the inheritance.

In fact this is exactly how class inheritance is implemented by the compilers - they create objects of superclass and provide the wrappers around them, only they do it in the binary code and I am forced to do the code generation.

Other readers were concerned that the resulting 'sub-classes' are not polymorphic - i.e. they cannot be used in place of their 'super-classes'. One can use the interfaces, however, to fix this problem as we are going to show below.

There are some limitations to this type of inheritance, however:

  1. This type of inheritance cannot have virtual functions used in 'super-class' overridden in 'sub-class'. The C# does not allow me to change the function pointers so creating virtual functions would requre a more invasive - binary code generation approach.
  2. And a related issue is that this inheritance does not allow using correct this pointer within 'super-classes' - this within a 'super-class' will be the wrapped object, not the wrapper object. This is not a big deal when it comes to properties or methods - since the properties and methods of a 'super-class' should not know about the 'sub-class' anyways. But it can create a big problem when it comes to the events containing this pointer as an argument. "One Way" event wrapping described below will show how to get around this problem.

 

In general, in spite of these two limitations this type of inhertiance can be very useful, as I hope to prove to you below.

In the "Refresher" sample above, we had PersonWrapper class 'inheriting' from Person class - which is a single class inhertitance. Nothing, however, prevents us from using several 'base' classes thus imitating multiple inheritance as the sample below are going to show.

Simple Multiple Inheritance with Polymorphism Sample

The purpose of this sample is to show how Polymorphism can be implemented using our inheritance and interfaces.

The sample code is located under SimplePolymorphism solution.

The main project contains two very simple interfaces IPerson and ISelectable:

public interface IPerson
{
    string Name { get; set; }

    void PrintInfoToConsole();
}
public interface ISelectable
{
    bool IsItemSelected { get; set; }

    event Action<iselectable> ItemSelectedChangedEvent;
}  
</iselectable>

There are two (also very simple) implementations of these two interfaces: Person and Selectable correspondingly. In particular the implementation of Person's PrintInfoToConsol() method, prints the line "Name = ", where "" should be replaced by the Name property of Person object:

public void PrintInfoToConsole()
{
    Console.WriteLine("Name = " + Name);
}  

Static method Program.PrintPersonInfoToConsol(IPerson person) of the main Program class calls PrintInfoToConsole() method of the IPerson object passed to it:

static void PrintPersonInfoToConsole(IPerson person)
{
    person.PrintInfoToConsole();
}  

Here is the definition of the wrapper class SelectablePerson (extends - in our sense - classes Person and Selectable):

[Wraps(typeof(Selectable), WrappedItemsKind.Event, "ItemSelectedChangedEvent")]
[Wraps(typeof(Selectable), WrappedItemsKind.Property, "IsItemSelected")]
[Wraps(typeof(Selectable), WrappedItemsKind.Method, "ToggleIsSelected")]
[Wraps(typeof(Person), WrappedItemsKind.Property, "Name")]
[Wraps(typeof(Person), WrappedItemsKind.Method, "PrintInfoToConsole")]
public partial class SelectablePerson : IPerson, ISelectable
{

}  

As you can see the generated wrapper members also make it implement IPerson and ISelectable interfaces. As an IPerson objects of this class can be passed to Program.PrintPersonInfoToConsol(IPerson person) method.

Take a look at Program class - we create a SelectablePerson object and pass it to Program.PrintPersonInfoToConsol(IPerson person) method:

SelectablePerson selectablePerson = new SelectablePerson
{
    ThePerson = new Person { Name = "Joe" },
    TheSelectable = new Selectable()
};

PrintPersonInfoToConsole(selectablePerson);  

As expected, when running the code above, we'll get "Name = Joe" printed to the console.

What if we want to create another class implementing IPerson and ISelectable with a different implementation of its PrintInfoToConsole() method - say instead of printing "Name = Joe" it will have to print "Hello Joe". Class SelectablePersonWithModifiedMethod shows how to do it:

[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 SelectablePersonWithModifiedMethod : IPerson, ISelectable
{
    public void PrintInfoToConsole()
    {
        Console.WriteLine("Hello " + this.Name);
    }
}  

We do not have a Wraps attribute for PrintInfoToConsole() method and instead provide its implement explicitly.

Creating such object and passing it to Program.PrintPersonInfoToConsole method will result in "Hello Joe" printed:

SelectablePersonWithModifiedMethod selectablePersonWithModifiedMethod = new SelectablePersonWithModifiedMethod
{
    ThePerson = new Person { Name = "Joe" },
    TheSelectable = new Selectable()
};

PrintPersonInfoToConsole(selectablePersonWithModifiedMethod); 

As you can see - interface polymorphism stands - the correct method is being called on the objects inherited in our sense.

One Way Event Wrapper Generation with this Reference Substitution

For people who know WPF, one of the most inglorious examples of C# limitation is its inability to factor out the code related to defining and firing the PropertyChanged event in the view models in a generic fashion. To be sure one can define a PropertyChangedImpl class as following:

public class PropertyChangedImpl : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(string propName)
    {
        if (PropertyChanged == null)
            return;

        PropertyChanged(this, new PropertyChangedEventArgs(propName));
    }
}  

Then all of your View Models can inherit from PropertyChangedImpl class. But because of the lack of Multiple Inheritance in C#, inhertiting PropertyChangedImpl class will prevent you from inheriting from any other class so, many times you will decide that it is not worth and and simply copy and paste the PropertyChangedImpl code.

In this section I describe how to mitigate this problem using wrapper generation inhertitance and also describe a new concept of One Way Event wrapper generation I stumbled upon because while figuring out how to make PropertyChangedImpl inheritance work.

Take a look at PropertyChnagedWrapperSample project. Its View Model MyViewModel class contains only one property TheText that calls method OnPropertyChanged which is a wrapper over the same named method of PropertyChangedImpl class:

[Wraps(typeof(PropertyChangedImpl), WrappedItemsKind.Event, "PropertyChanged")]
[Wraps(typeof(PropertyChangedImpl), WrappedItemsKind.Method, "OnPropertyChanged")]
public partial class MyViewModel : INotifyPropertyChanged
{
    //public event PropertyChangedEventHandler PropertyChanged;

    public MyViewModel()
    {
        this.ThePropertyChangedImpl = new PropertyChangedImpl();

        //this.ThePropertyChangedImpl.PropertyChanged += ThePropertyChangedImpl_PropertyChanged;
    }

    //private void ThePropertyChangedImpl_PropertyChanged(object sender, PropertyChangedEventArgs e)
    //{
    //    if (PropertyChanged != null)
    //    {
    //        PropertyChanged(this, e);
    //    }
    //}

    string _text = null;
    public string TheText
    {
        get
        {
            return _text;
        }

        set
        {
            if (_text == value)
                return;

            _text = value;

            OnPropertyChanged("TheText");
        }
    }
}  

MainWindow.xaml file defined an instance of the View Model within its Resources section and also also defines a TextBlock above and a TextBox below. Text properties of the TextBlock and TextBox are both bound to TheText property of the View Model so if the bindings work and PropertyChanged event if firing properly, whatever is entered within the TextBox should appear in the TextBlock above:

<window.resources>
    <local:myviewmodel x:key="TheViewModel">
</local:myviewmodel></window.resources>
<grid datacontext="{Binding Source={StaticResource TheViewModel}}">
    <stackpanel horizontalalignment="Center" orientation="Vertical" verticalalignment="Center">
        <textblock text="{Binding TheText}">

        <stackpanel orientation="Horizontal">
            <textblock text="Enter Text: ">
            <textbox text="{Binding TheText, UpdateSourceTrigger=PropertyChanged}">
        </textbox></textblock></stackpanel>
    </textblock></stackpanel>
</grid>  

If we run the project as it is, however, we'll notice that the TextBlock above is not updated.

What is broken here is the PropertyChanged event firing - in particular the handling of this reference passed to it. Indeed OnPropertyChanged(...) called on MyViewModel class will call the OnPropertyChanged(...) method of the wrapped PropertyChangedImpl object, which will fire PropertyChanged event on that object, passing to it this reference with this meaning PropertyChangedImpl object and not the MyViewModel wrapper:

PropertyChanged(this, new PropertyChangedEventArgs(propName));  

Because of that, the binding - which is set on the MyViewModel object does not notice the PropertyChanged event firing.

There is a way to fix the problem, though: comment out the PropertyChanged event's Wrap attribute and uncomment all the commented out lines so that MyViewModel class will look like this:

using NP.WrapperAttrs;
using System.ComponentModel;


namespace PropertyChangedWrapperSample
{
    //[Wraps(typeof(PropertyChangedImpl), WrappedItemsKind.Event, "PropertyChanged")]
    [Wraps(typeof(PropertyChangedImpl), WrappedItemsKind.Method, "OnPropertyChanged")]
    public partial class MyViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public MyViewModel()
        {
            this.ThePropertyChangedImpl = new PropertyChangedImpl();

            this.ThePropertyChangedImpl.PropertyChanged += ThePropertyChangedImpl_PropertyChanged;
        }

        private void ThePropertyChangedImpl_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, e);
            }
        }

        string _text = null;
        public string TheText
        {
            get
            {
                return _text;
            }

            set
            {
                if (_text == value)
                    return;

                _text = value;

                OnPropertyChanged("TheText");
            }
        }
    }
}  

Try running the sample again after making those changes. Now everything works and when you type "Hello" in the TextBox you will also see Hello displayed above:

Let us take a look as what's happening here. Instead of MyViewModel.PropertyChanged event being a perfect wrapper of PropertyChangedImpl.PropertyChanged event, we define it to be a separate entity that is fired when PropertyChangedImpl.PropertyChanged event is fired. Moreover we replace the first argument if the event with this reference. Since this replacement is done within MyViewModel class, the correct object is passed to it:

public MyViewModel()
{
    this.ThePropertyChangedImpl = new PropertyChangedImpl();

    this.ThePropertyChangedImpl.PropertyChanged += ThePropertyChangedImpl_PropertyChanged;
}

private void ThePropertyChangedImpl_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (PropertyChanged != null)
    {
        PropertyChanged(this, e);
    }
}  

This is what I call One Way Event Wrapping with this Reference Substitution (and if someone can come with a better and more succinct name - I'll be happy to consider it:-))

I added OneWayEventWraps attribute and the functionality to generate One Way Event related wrappers into the new version of NP.WrapperGenerator.vsix extension.

In order to see the new attribute in action, please take a look at PropertyChangedOneWayEventWrapSample project. It is very similar to PropertyChnagedWrapperSample project considered above. The only difference is that instead of code that we uncommented in MyViewModel class we use OneWayEventWraps extension. Here is how the MyViewModel class looks now:

[OneWayEventWraps(typeof(PropertyChangedImpl), "PropertyChanged", "sender")]
[Wraps(typeof(PropertyChangedImpl), WrappedItemsKind.Method, "OnPropertyChanged")]
public partial class MyViewModel : INotifyPropertyChanged
{
    public MyViewModel()
    {
        this.ThePropertyChangedImpl = new PropertyChangedImpl();
    }

    string _text = null;
    public string TheText
    {
        get
        {
            return _text;
        }

        set
        {
            if (_text == value)
                return;

            _text = value;

            OnPropertyChanged("TheText");
        }
    }
}  

OneWayEventWraps attribute takes the wrapped object ('base' class) as its first argument. Second argument is the name of the wrapped event - PropertyChanged in our case. Third argument is optional (only if you want to replace this reference) and it specifies the name of the event delegate's argument to replace with this reference. In our case, the event's delegate is of type void (object sender, PropertyChangedEventArgs e), so we want to replace its argument called "sender" with this reference.

Here is the resulting generated code from MyViewModel.wrapper.cs file - I only show parts relevant for the One Way Event wrapping (also I added some comments explaining the code):

...
// declare the PropertyChanged event within the wrapper class
public event PropertyChangedEventHandler PropertyChanged;

public PropertyChangedImpl ThePropertyChangedImpl
{
    get
    {
        return _propertyChangedImpl;
    }
    set
    {
        if ((_propertyChangedImpl != null))
        {
            // remove event handler to the old 'base' object's PropertyChanged event
            _propertyChangedImpl.PropertyChanged -= _propertyChangedImpl_PropertyChanged;
        }
        _propertyChangedImpl = value;
        if ((_propertyChangedImpl != null))
        {
            // added event handler to the new 'base' object's PropertyChanged event
            _propertyChangedImpl.PropertyChanged += _propertyChangedImpl_PropertyChanged;
        }
    }
}  

// the event handler
private void _propertyChangedImpl_PropertyChanged(Object sender, PropertyChangedEventArgs e)
{
    if ((PropertyChanged != null))
    {
        // replaced 'sender' object with 'this' reference.
        PropertyChanged(this, e);
    }
}
...

As will be shown in part 2 of this article - there are many more uses of One Way Event Wrapping than just for PropertyChanged event.

Conclusion

In this article we presented some cases of simple polymorphic wrapper-based imitated Multiple Inheritance and also talked about defined and presented One Way Event Wrapping.

Next installment of this article will talk about more complex cases of Multiple Inheritance polymorphism and about resolving the diamond Multiple Inheritance.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here