C# Wrapping Hierarchy — Digitteck
C# Wrapping Hierarchy
dotnet·15 February 2019·2 min read

C# Wrapping Hierarchy

Limiting the exposure of third-party libraries is a good practice — you create your own wrapper around an external type so the rest of your application never touches the original directly. Wrapping is similar to the adapter pattern (but in reverse). The challenge appears when the library you are wrapping has its own inheritance hierarchy: you want your wrappers to mirror that hierarchy so they can be grouped and iterated polymorphically.

Third-Party Library

Assume the external library provides two classes that both derive from a common base:

csharp
// Third-party library: two concrete classes sharing a base
public abstract class TPModel
{
    public abstract string Name { get; set; }
}

public class TPModelChild1 : TPModel
{
    public override string Name { get; set; }
    public string ExtraProperty { get; set; }
}

public class TPModelChild2 : TPModel
{
    public override string Name { get; set; }
    public string ExtraProperty { get; set; }
}

Wrapper Interface

A generic interface parameterised on the original type. Every wrapper must expose the underlying third-party instance through Get():

csharp
// Generic wrapper interface — exposes the wrapped third-party type
public interface IWrapper<TOriginal> where TOriginal : class
{
    TOriginal Get();
}

Wrapped Models

To keep the relationship between wrapped models, each wrapper implements the interface twice — once for the concrete child type and once for the base type. The base-type implementation uses explicit interface implementation and delegates to the typed overload:

csharp
// Implement the interface twice: once for the concrete child, once for the base.
// The dual implementation preserves the hierarchy in the wrapper layer.
public class WrapperChildOne : IWrapper<TPModelChild1>, IWrapper<TPModel>
{
    public string Name { get; set; }
    public string ExtraProperty { get; set; }

    public TPModelChild1 Get()
    {
        return new TPModelChild1
        {
            Name = this.Name,
            ExtraProperty = this.ExtraProperty
        };
    }

    // Explicit implementation — delegates to the typed overload above
    TPModel IWrapper<TPModel>.Get() => this.Get();
}
csharp
public class WrapperChildTwo : IWrapper<TPModelChild2>, IWrapper<TPModel>
{
    public string Name { get; set; }
    public string ExtraProperty { get; set; }

    public TPModelChild2 Get()
    {
        return new TPModelChild2
        {
            Name = this.Name,
            ExtraProperty = this.ExtraProperty
        };
    }

    TPModel IWrapper<TPModel>.Get() => this.Get();
}

Grouping the Wrappers

Because both wrappers share the IWrapper<TPModel> interface, they can be placed in the same collection and iterated without knowing their concrete types:

csharp
// Both wrappers share IWrapper<TPModel> — group them in a single typed collection
WrapperChildOne wrapperOne = new WrapperChildOne { Name = "aa", ExtraProperty = "bb" };
WrapperChildTwo wrapperTwo = new WrapperChildTwo { Name = "aa", ExtraProperty = "bb" };

List<IWrapper<TPModel>> listOfWrappers = new List<IWrapper<TPModel>>
{
    wrapperOne,
    wrapperTwo
};

// Iterate without knowing the concrete type
foreach (IWrapper<TPModel> wrapper in listOfWrappers)
{
    TPModel model = wrapper.Get();
    Console.WriteLine(model.Name);
}

Tags

.NETC#Design Patterns
digitteck

© 2026 Digitteck