Click here to Skip to main content
13,771,105 members
Click here to Skip to main content
Add your own
alternative version

Tagged as

(untagged)

Stats

3.2K views
6 bookmarked
Posted 25 Feb 2018
Licenced CPOL

Roxy: Strong Types and Method Overloading

, 25 Feb 2018
Rate this:
Please Sign up or sign in to vote.
describes Strong Typing and overloading functionality recently added to Roxy.

Introduction

I introduced Roxy as a Proxy Generation and (in the near future also) and IoC Container in Introducing Roxy: Powerful Proxy Generation and IoC Container Package and Separation of Concerns and Smart Mixins with the help of Roxy IoC Container and Code Generator.

Name Roxy is derived from two words - Roslyn (the new MS compiler as a service package) and Proxy.

The main reason behind creating Roxy is to enable better separation of concerns using, what I call, 'smart mixins' - mixins that allow renaming and merging of the events, properties and methods. This feature is described in Separation of Concerns and Smart Mixins with the help of Roxy IoC Container and Code Generator article.

I hope that such mixins will eventually replace the implementation inheritance in software languages. In fact, time permitting, I plan to create my own Roslyn modification that would make such feature part of the expanded C# language.

On top of the smart mixins - Roxy also facilitate creating wrapper/adapters and proxy patterns as well as accessing the non-public functionality of 3rd party packages (something that should be definitely used only by experts). These features are described in Introducing Roxy: Powerful Proxy Generation and IoC Container Package article.

Up to a couple of days ago, the Roxy mixins were name based and correspondingly had weak typing and no method overloading was allowed. Now I've completed LINQ based strongly typed functionality, which also allows overloading. In this article I present the demos that explain this new functionality.

I tried making this article as self contained as possible, but it is still advisable to read the previous two articles on Roxy.

Samples

Code Location

The demo code can be downloaded via the link above the article. Also the code exists as a repository NP.Roxy.StrongTypingDemos on github.

The code depends on NP.Roxy nuget package downloadable from nuget.org and on Roslyn (Microsoft.CodeAnalysis) packages. All the packages should automatically download during the first build as long as you have a working internet connection.

Composite Property Getter Sample

Composite Property Getter sample is located under NP.Roxy.CompositeGetters solution.

This sample shows how to set up a property getter for a composite property - a property that depends on other properties or methods of the same interface.

We use Roxy in order to implement a very simple interface IMyData:

public interface IMyData
{
    string LastName { get; set; }

    string FirstName { get; set; }

    // we create FullName using a lambda expression
    string FullName { get; }
}  

Properties LastName and FirstName are implemented as simple auto properties (which is a default in Roxy) while the property FullName is implemented by using a LINQ Expression.

Here is the code of Program.Main which uses Roxy to generate the type and then tests object of that type:

static void Main(string[] args)
{
    // ITypeConfig is an interface 
    // that can be configured to generate the code
    // for a specific type. 
    // The Template Argument IMyData specifies
    // the interface to implement.
    // SecondArgument NoInterface means
    // that there are no wrapper objects.
    ITypeConfig typeConfig =
        Core.FindOrCreateTypeConfig<IMyData, NoInterface>();

    // set up the lambda expression for IMyData.FullName Property
    // implementation
    typeConfig.SetPropGetter<IMyData, string>
    (
        // this expression specified the name
        // of the property to implement
        (data) => data.FullName,

        // this is the property implementation expression:
        // Full
        (data) => data.LastName + ", " + data.FirstName
    );

    // specify that the type configuration is completed
    typeConfig.ConfigurationCompleted();

    // Get an instance of IMyData generated implementation
    // type. 
    IMyData myData = Core.GetInstanceOfGeneratedType<IMyData>();

    // set FirstName
    myData.FirstName = "Joe";

    // set LastName
    myData.LastName = "Doe";

    // Print FullName
    Console.WriteLine(myData.FullName);

    // save the Roxy generated project under GeneratedCode folder (within the 
    // directory that contains the executable)
    Core.Save("GeneratedCode");
}  

The program will print the FullName property value "Doe, Joe" to the console.

The most important code here is:

// set up the lambda expression for IMyData.FullName Property
// implementation
typeConfig.SetPropGetter<IMyData, string>
(
    // this expression specified the name
    // of the property to implement
    (data) => data.FullName,

    // this is the property implementation expression:
    // Full
    (data) => data.LastName + ", " + data.FirstName
);  

The first argument is an expression that specifies the name of the property "FullName" whose getter is being set. The second argument specifies the actual expression that forms the getter.

Important Note I am generating the code and insert it into the generated class based on the LINQ expression - I am not inserting the lambda itself - since i thought that would be too confusing - the class will have some static lambdas but the code for them would be specified in the class builder area. Because of that, the closure will not work - the expression should only consist of the class properties, some static functionality, visible from the class and, perhaps, some constants. This is something I might change in the future - I might provide the actual lambdas for calculation also generating the expression code as a comment for clarity. In that case, the generated code will be more confusing, but the closures will work 100%.

Turning Enumeration into an Interface using Strongly Typed LINQ Expression

In the first Roxy article Introducing Roxy: Powerful Proxy Generation and IoC Container Package, I showed how to turn an enumeration into an interface. The enumeration ProductKind had an extension class ProductKindExtensions that was providing static extension methods static string GetDisplayName(this ProductKind productKind) and static string GetDescription(this ProductKind productKind) that mapped into the same named methods of the interface.

The strongly typed counterpart of this sample is located under NP.Roxy.StrongTypeEnumTest solution.

Using this, strongly typed LINQ based API we can map those extension methods to properties (not necessarily methods) on the interface.

Here is the code ProductKind enum and its extension class ProductKindExtensions:

public enum ProductKind
{
    Grocery,
    FinancialInstrument,
    Information
}

public static class ProductKindExtensions
{
    // returns a displayable short name for the ProductKind
    public static string GetDisplayName(this ProductKind productKind)
    {
        switch (productKind)
        {
            case ProductKind.Grocery:
                return "Grocery";
            case ProductKind.FinancialInstrument:
                return "Financial Instrument";
            case ProductKind.Information:
                return "Information";
        }

        return null;
    }

    // returns the full description of the ProductKind
    // note that the method is private
    public static string GetDescription(this ProductKind productKind)
    {
        switch (productKind)
        {
            case ProductKind.Grocery:
                return "Products you can buy in a grocery store";
            case ProductKind.FinancialInstrument:
                return "Products you can buy on a stock exchange";
            case ProductKind.Information:
                return "Products you can get on the Internet";
        }

        return null;
    }
}  

The interface IProduct that we want to implement contains only two properties that we want to map into the extension methods:

public interface IProduct
{
    string DisplayName { get; }

    string Description { get; }
}  

Here is the documented code of Program.Main:

static void Main(string[] args)
{
    // create the ITypeConfig for implementing the 
    // IProduct interface based on ProductKind enumeration
    ITypeConfig<SingleWrapperInterface<ProductKind>> adapterTypeConfig =
        Core.FindOrCreateSingleWrapperTypeConfig<IProduct, ProductKind>();

    // set IProduct.DisplayName property to be implemented 
    // as ProductKindExtensions.GetDisplayName() extension method
    // on the product kind value
    adapterTypeConfig.SetWrappedPropGetter<IProduct, ProductKind, string>
    (
        prod => prod.DisplayName,
        prodKind => prodKind.GetDisplayName()
    );

    // set IProduct.Description property to be implemented
    // by calling ProductKindExtensions.GetDescription() extension method
    // on the product kind value
    adapterTypeConfig.SetWrappedPropGetter<IProduct, ProductKind, string>
    (
        prod => prod.Description,
        prodKind => prodKind.GetDescription()
    );

    // complete configuration and generate the code
    adapterTypeConfig.ConfigurationCompleted();

    // get IProduct for ProductKind.Information enum value
    IProduct product = Core.GetInstanceOfGeneratedType<IProduct>(null, ProductKind.Information);

    // write <DisplayName>: <Description>
    Console.WriteLine($"{product.DisplayName}: {product.Description}");

    // save the Roxy generated project under GeneratedCode folder (within the 
    // directory that contains the executable)
    Core.Save("GeneratedCode");
}  

When you run the sample, you'll get the following printed to the console:

Information: Products you can get on the Internet  

"Information" is the DisplayName of the ProductKind.Information enumeration value and "Products you can get on the Internet" is its Description.

Strong Typed Method Wrapping

The sample's code is under NP.Roxy.StrongTypeMethodTest solution.

In this sample, we implement IMyData.GetGreeting(...) method using MyDataImpl.GetGreetingImpl(...) implementation.

Here is the code for IMyData interface and the implementing class MyDataImpl:

public interface IMyData
{
    string FirstName { get; set; }

    string LastName { get; set; }

    string GetGreeting(string greetingMessage);
}  

public abstract class MyDataImpl
{
    public abstract string FirstName { get; }

    public abstract string LastName { get; }

    // used to implement GetGreetings method of
    // IMyData interface. the greetingMessage is 
    // followed by first and last names. 
    public string GetGreetingImpl(string greetingMessage)
    {
        return $"{greetingMessage} {FirstName} {LastName}!";
    }
}

We use the wrapper class IWrapper to define the MyDataImpl wrapper object:

// defines a wrapper around MyDataImpl
// class
public interface IWrapper
{
    MyDataImpl TheDataImpl { get; }
}  

And here is the Program.Main(...) method:

class Program
{
    static void Main(string[] args)
    {
        // saves the generated files in case of a compilation error
        Core.SetSaveOnErrorPath("GeneratedCode");

        // created the ITypeConfig for configuring the IMyData implementation
        // using IWrapper interface
        ITypeConfig<IWrapper> typeConfig = Core.FindOrCreateTypeConfig<IMyData, IWrapper>();

        // sets the GetGreetings method of IMyData
        // interface to MydataImplGet.GreetingImpl implementation
        // the two last type arguments signify the string argument that is
        // passed to the method and the string output argument. 
        typeConfig.SetReturningMethodMap<IMyData, MyDataImpl, string, string>
        (
            // specifies the interface method to implement
            (data, inputStr) => data.GetGreeting(inputStr),

            // specifies the wrapper object that used
            // to implement the interface method
            (wrapper) => wrapper.TheDataImpl,

            // specifies the method implementation
            (dataImpl, inputStr) => dataImpl.GetGreetingImpl(inputStr)
        );

        // code is generated
        typeConfig.ConfigurationCompleted();

        // we get the instance of the generated class
        // that implements IMyData interface
        IMyData myData = Core.GetInstanceOfGeneratedType<IMyData>();

        // set the first and last names
        myData.FirstName = "Joe";
        myData.LastName = "Doe";

        // get the greeting by calling IMyData.GetGreeting method of 
        // the interface, passing "Hello" string to it
        string greetingStr = myData.GetGreeting("Hello");

        // Prints the resulting string 
        // "Hello Joe Doe!"
        Console.WriteLine(greetingStr);

        // saves the generated code in case of successful 
        // completion. 
        Core.Save("GeneratedCode");
    }  
}

Here is the actual method that does the mapping:

// sets the GetGreetings method of IMyData
// interface to MydataImplGet.GreetingImpl implementation
// the two last type arguments signify the string argument that is
// passed to the method and the string output argument. 
typeConfig.SetReturningMethodMap<IMyData, MyDataImpl, string, string>
(
    // specifies the interface method to implement
    (data, inputStr) => data.GetGreeting(inputStr),

    // specifies the wrapper object that used
    // to implement the interface method
    (wrapper) => wrapper.TheDataImpl,

    // specifies the method implementation
    (dataImpl, inputStr) => dataImpl.GetGreetingImpl(inputStr)
);  

Method Overloading

Take a look at NP.Roxy.MethodOverloadingTest solution.

IMyData interface declares two methods with the same name but different signature IMyData.GetGreeting() and IMyData.GetGreeting(string greetingMessage).

Correspondingly two the implementation class MyDataImpl defines two implementations of the same name and signature: IMyData.GetGreeting() and IMyData.GetGreeting(string greetingMessage):

public interface IMyData
{
    string FirstName { get; set; }

    string LastName { get; set; }

    // demonstrates method overloading
    string GetGreeting();

    // demonstrates method overloading
    string GetGreeting(string greetingMessage);
}  

public abstract class MyDataImpl
{
    public abstract string FirstName { get; }

    public abstract string LastName { get; }

    // demonstrates method overloading
    public string GetGreeting() => "Hello World!";

    // demonstrates method overloading
    public string GetGreeting(string greetingMessage)
    {
        return $"{greetingMessage} {FirstName} {LastName}!";
    }
}

Here is the documented Program.Main code:

static void Main(string[] args)
{
    // saves the generated code on compilation error
    Core.SetSaveOnErrorPath("GeneratedCode");

    // creates the TypeConfig object
    // for IMyData interface to be implemented
    // using IWrapper.TheDataImpl
    ITypeConfig<IWrapper> typeConfig = 
        Core.FindOrCreateTypeConfig<IMyData, IWrapper>();

    // completes the configuration,
    // generates the code.
    typeConfig.ConfigurationCompleted();

    // creates the object of generated class that
    // implements IMyData interface
    IMyData myData = Core.GetInstanceOfGeneratedType<IMyData>();

    // sets first and last name
    myData.FirstName = "Joe";
    myData.LastName = "Doe";

    // calls GetGreeting() method
    string greetingStr1 = myData.GetGreeting();

    // writes "Hello World1"
    Console.WriteLine(greetingStr1);

    // calls GetGreeting("Hello") method
    string greetingStr2 = myData.GetGreeting("Hello");

    // prints "Hello Joe Doe!"
    Console.WriteLine(greetingStr2);

    // saves the generated code in case of 
    // successful completion. 
    Core.Save("GeneratedCode");
}  

Both overloaded methods of the interface are getting correct implementations from the overloaded methods of the class. As a result the following will be printed:

Hello World!
Hello Joe Doe!  

Conclusion

The samples above demonstrate Strong Typing and overloading functionality newly added to NP.Roxy software.

License

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

Share

About the Author

Nick Polyak
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 have my Ph.D. from RPI.

here is my linkedin profile - I'll be happy to connect!

You may also be interested in...

Comments and Discussions

 
-- There are no messages in this forum --
Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web01-2016 | 2.8.181119.1 | Last Updated 25 Feb 2018
Article Copyright 2018 by Nick Polyak
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid