Click here to Skip to main content
13,501,050 members
Click here to Skip to main content
Add your own
alternative version

Stats

4K views
3 bookmarked
Posted 20 Nov 2017

Injecting an interface into unrelated types via partial classes

, 20 Nov 2017
Rate this:
Please Sign up or sign in to vote.
This how-to article explores an approach using partial classes to inject a common interface into unrelated types that are auto-generated from web services or other external sources

Introduction

Recently I was working on some code that was pulling data objects off a remote WCF OAuth API. Visual Studio auto-generated the proxy classes for the data entities, and it was all good. There was one glitch though. While the various entity classes had several common properties such as IsDeleted, IsActive, etc. they did not share a base class or interface. So while we had methods that looped through these objects and did pretty much identical tasks against these properties, we could not use a common method as these were seemingly unrelated classes. Luckily, they were all partial classes, which meant we could use that to inject a common base interface to these classes. This article describes this approach.

Example code

This is a dumbed down example, so it will seem rather contrived. Use your imagination to transform this to a more realistic scenario.

namespace LibraryY
{
    public partial class ObjectY
    {
        public bool IsDeleted { get; set; }

        public bool IsDisabled { get; set; }

        public bool IsInActive { get; set; }

        public static ICollection<ObjectY> GetItems()
        {
            return new[] { new ObjectY() };
        }
    }
}

And here's a second unrelated class with no common base interface or class. Even in a different namespace.

namespace LibraryX
{
    public partial class ObjectX
    {
        public bool IsDeleted { get; set; }

        public bool IsDisabled { get; set; }

        public bool InActive { get; set; }

        public static ICollection<ObjectY> GetItems()
        {
            return new[] { new ObjectX() };
        }
    }
}

Notice how they have two properties that have identical names.

  • IsDeleted
  • IsDisabled

They also have a property each, which represents the same logical meaning but are named differently

  • ObjectX.InActive
  • ObjectY.IsInActive

The partial class trick works for the first scenario, same names. For the different-names scenario, I discuss a different approach for the sake of completion. But that's not set in stone, so you may have other preferred workarounds.

Injecting the interface

Essentially, there are two steps here. Step 1 is to define an interface with the commonly named properties.

namespace InterfaceInjector
{
    interface ICommonInterface
    {
        bool IsDeleted { get; set; }
        bool IsDisabled { get; set; }
    }
}

And step 2 is to use partial classes to extend the unrelated classes and apply this interface

namespace LibraryX
{
    public partial class ObjectX : InterfaceInjector.ICommonInterface { }
}

namespace LibraryY
{
    public partial class ObjectY : InterfaceInjector.ICommonInterface { }
}

That's it, really. Here's some test code showing this in action

    var xItems = LibraryX.ObjectX.GetItems();

    foreach (var item in xItems)
    {
        Handle(item);
    }

    var yItems = LibraryY.ObjectY.GetItems();

    foreach (var item in yItems)
    {
        Handle(item);
    }

// . . . 

private static void Handle(InterfaceInjector.ICommonInterface item)
{
    if(item.IsDeleted)
    {
        // handle delete
    }

    if(item.IsDisabled)
    {
        // handle disable
    }
}

So, we have a common shared method processing objects that are unrelated. Well, techncially we've forced them to be in a sort of relationship here. Alright, so what about the other scenario? As mentioned, you can do this is multiple ways, and here's one easy way to do this. This approach defines a wrapper class per differently named property in the unrelated classes that all work the same way logically (this is for you to ascertain).

interface IActiveStatus
{
    bool IsInActive { get; set; }
}

public class ActiveStatusWrapper<T> : IActiveStatus
{
    T item;
    Func<bool> getter;
    Action<bool> setter;

    public ActiveStatusWrapper(T item, Func<bool> getter, Action<bool> setter)
    {
        this.item = item;
        this.getter = getter;
        this.setter = setter;
    }

    public bool IsInActive
    {
        get
        {
            return getter();
        }

        set
        {
            setter(value);
        }
    }

    public T Item { get { return item; } }
}

Now you would just need to define a method to handle this new interface.

private static void Handle(IActiveStatus activeStatusItem)
{
    if(activeStatusItem.IsInActive)
    {
        // handle inactive
    }
}

When passing the objects to the method, create an instance of the wrapper and pass the getter/setter methods.

var xItems = LibraryX.ObjectX.GetItems();

foreach (var item in xItems)
{
    Handle(item);
    Handle(new ActiveStatusWrapper<ObjectX>(item, 
        () => item.InActive, (v) => item.InActive = v));        
}

var yItems = LibraryY.ObjectY.GetItems();

foreach (var item in yItems)
{
    Handle(item);
    Handle(new ActiveStatusWrapper<ObjectY>(item, 
        () => item.IsInActive, (v) => item.IsInActive = v));
}

You can clean this up a little better by using reflection to invoke the getter/setter methods. That way you don't need to instantiate function and action objects for each object. That said, compared with the additional cost of relection, you may want to compare the two approaches for performance before making a call there.

License

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

Share

About the Author

Nish Nishant
United States United States
Nish Nishant is a Principal Software Architect based out of Columbus, Ohio. He has over 17 years of software industry experience in various roles including Lead Software Architect, Principal Software Engineer, and Product Manager. Nish was a Microsoft Visual C++ MVP between 2002 and 2015.

Nish is an industry acknowledged expert in the Microsoft technology stack. He authored C++/CLI in Action for Manning Publications in 2005, and had previously co-authored Extending MFC Applications with the .NET Framework for Addison Wesley in 2003. In addition, he has over 140 published technology articles on CodeProject.com and another 250+ blog articles on his WordPress blog. Nish is vastly experienced in team management, mentoring teams, and directing all stages of software development.

Contact Nish : If you are interested in hiring Nish as a consultant, you can reach him via his google email id voidnish.

Company Website : www.ganymedesoftwaresolutions.com

You may also be interested in...

Pro

Comments and Discussions

 
Questionpublic static ICollection<ObjectY> GetItems() Pin
eng.RHKhan7-Feb-18 1:46
membereng.RHKhan7-Feb-18 1:46 
QuestionThis only works inside a single assembly and isn't really injecting anything. Pin
Dismember22-Nov-17 21:45
memberDismember22-Nov-17 21:45 
AnswerRe: This only works inside a single assembly and isn't really injecting anything. Pin
Nish Nishant23-Nov-17 4:35
sitebuilderNish Nishant23-Nov-17 4:35 
QuestionImprove IActiveStatus implementation Pin
InbarBarkai20-Nov-17 19:27
memberInbarBarkai20-Nov-17 19:27 
AnswerRe: Improve IActiveStatus implementation Pin
Nish Nishant21-Nov-17 0:46
sitebuilderNish Nishant21-Nov-17 0:46 
GeneralRe: Improve IActiveStatus implementation Pin
Member 1127135820-Dec-17 21:48
memberMember 1127135820-Dec-17 21:48 

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.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.180417.1 | Last Updated 20 Nov 2017
Article Copyright 2017 by Nish Nishant
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid