3

I have an class object from an external library that I want to add some additional properties to.

Let's say the external class is:

public class ExternalClass
{
    public string EXproperty1 {get;set;}
    public string EXproperty2 {get;set;}
    public string EXproperty3 {get;set;}

    public ExternalClass(){}
}

and I have a list of these object which gets populated as

List<ExternalClass> listOfExternalClass=new List<ExternalClass>();
listOfExternalClass=GetListOfExternalClass();

I can extend this class by creating a new class, adding the additional properties and making the external class a property.

public class NewClass
{
    public ExternalClass ExternalClass {get;set;}
    public string NewProperty1 {get;set;}
    public string NewProperty2 {get;set;}

    public NewClass(){}
    public NewClass(ExternalClass externalClass){
        this.ExternalClass=externalClass;
    }
}

But to convert by original list of the external classes to a list of the new classes I would have to create a new list of new classes and iterate through the original list creating a new object and adding it to the list, like

List<NewClass> listOfNewClass=new List<NewClass>();
foreach(var externalClass in listOfExternalClass)
{
    listOfNewClass.Add(new NewClass(externalClass));
}

I would then be able to access the external properties like

listOfNewClass.FirstOrDefault().ExternalClass.EXproperty1;

Can I do this with inheritance or is there a more efficient method?

Ideally I would like to end up with by calling the properties like:

listOfNewClass.FirstOrDefault().EXproperty1;
klashar
  • 2,519
  • 2
  • 28
  • 38
DeclanMcD
  • 1,518
  • 4
  • 22
  • 41

5 Answers5

7

This can certainly be done with inheritance. Consider the following.

//Inherit from our external class
public class NewClass: ExternalClass
{
    //Note we do not have a copy of an ExternalClass object here.
    //This class itself will now have all of its instance members.
    public string NewProperty1 {get;set;}
    public string NewProperty2 {get;set;}

    //If it has parameters include those parameters in NewClass() and add them to base().
    //This is important so we don't have to write all the properties ourself.
    //In some cases it's even impossible to write to those properties making this approach mandatory.
    public NewClass()
    {

    }
}

Few things to know:

  • Your code is called a wrapper. This is because it "wraps" another class or group of classes.
  • You cannot inherit from class marked as sealed.
  • In C# classes are not sealed by default. If they're sealed the developer has intentionally prevented you from inheriting from the class. This is usually for a good reason.
Licht
  • 1,079
  • 1
  • 12
  • 27
4

If you can actually extend the External class that would be easy to accomplish:

public class NewClass: ExternalClass
{
    public string NewProperty1 {get;set;}
    public string NewProperty2 {get;set;}

    public NewClass(){}
    public NewClass(ExternalClass externalClass){
        // you would have to copy all the properties
        this.EXproperty1 = externalClass.EXproperty1;
    }
}
John Smith
  • 2,282
  • 1
  • 14
  • 22
  • This is what I started doing but there are way too many properties to map. It will also prove problematic if any of the properties in the external class change or are added/removed. – DeclanMcD Feb 24 '17 at 13:17
  • Aha, maybe something completely dynamic, but that might prove too complicated: http://stackoverflow.com/questions/3810488/adding-a-property-to-an-existing-class – John Smith Feb 24 '17 at 13:20
  • or to automatically copy all properties see: http://stackoverflow.com/questions/930433/apply-properties-values-from-one-object-to-another-of-the-same-type-automaticall – John Smith Feb 24 '17 at 13:26
  • This is what I ended up doing. The problem with this was that there were several fields not being exposed in the inherited calss for some reason, so I just used this.EXproperty1 = externalClass.EXproperty1; whenver this occurred. Thnaks – DeclanMcD Feb 27 '17 at 09:23
1

Yes inheritance is what you are looking for:

public class ExternalClass
{
   public string EXproperty1 { get; set; }
   public string EXproperty2 { get; set; }
   public string EXproperty3 { get; set; }

   public ExternalClass() { }
}

public class NewClass:ExternalClass
{            
    public string NewProperty1 { get; set; }
    public string NewProperty2 { get; set; }
    public NewClass() { }          
}
pquest
  • 3,151
  • 3
  • 27
  • 40
apomene
  • 14,282
  • 9
  • 46
  • 72
1

If you wish for (or need) delegation instead of a copy you can do:

public class NewClass
{
    public ExternalClass ExternalClass {get;set;}
    public string NewProperty1 {get;set;}
    public string NewProperty2 {get;set;}

    public string EXproperty1 {get { return this.ExternalClass.EXproperty1; };set{ this.ExternalClass.EXproperty1 = value; }; }
    public string EXproperty2 {get { return this.ExternalClass.EXproperty2; };set{ this.ExternalClass.EXproperty2 = value; }; }
    public string EXproperty3 {get { return this.ExternalClass.EXproperty3; };set{ this.ExternalClass.EXproperty3 = value; }; }

    public NewClass(){}
    public NewClass(ExternalClass externalClass){
        this.ExternalClass=externalClass;
    }
}
John Smith
  • 2,282
  • 1
  • 14
  • 22
  • +1 This is what I was busy typing and compliments @Licht's answer by filling in what can be done when classes are sealed. Only drawback is the lack of polymorphism. – David Culp Feb 24 '17 at 13:19
1

Instead of working against specific types, work against interfaces.

Below I am showing a mix of facade pattern and adapter pattern to 'transform' external data to a well-defined interface (IDocument), effectively abstracting things your are working on.

Example 1 : query about an interface

Here are the types you'll work against:

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

public interface IMetadata {
    string[] Tags { get; set; }
}

This is your own representation, should you need any:

public class RichDocument : IDocument, IMetadata {
    public string Name { get; set; }
    public string[] Tags { get; set; }
}

This is the wrapper against external data:

(a bastard mix of facade and/or adapter concepts)

public class ExternalClass {
    public string Whatever { get; set; }
}

public class ExternalDocument : IDocument /* only a basic object */ {
    private readonly ExternalClass _class;

    public ExternalDocument(ExternalClass @class) {
        _class = @class;
    }

    public string Name {
        get { return _class.Whatever; }
        set { _class.Whatever = value; }
    }
}

And a demo on how to use all that:

internal class Demo1 {
    public Demo1() {
        var documents = new List<IDocument> {
            new ExternalDocument(new ExternalClass()),
            new RichDocument()
        };

        foreach (var document in documents){
            var name = document.Name;
            Console.WriteLine(name);

            // see if it implements some interface and do something with it
            var metadata = document as IMetadata;
            if (metadata != null) {
                Console.WriteLine(metadata.Tags);
            }
        }
    }
}

Example 2 : query about a component

This is a bit more involved by pushing the concept to treat everything in an uniform manner, you can find it in .NET framework, game development or whatever ...

Definitions you'll work against:

public interface IContainer {
    IList<IComponent> Components { get; }
}

public interface IComponent {
    // it can be/do anything
}

Some components you'll query about:

public interface IDocument : IComponent {
    string Name { get; set; }
}

public interface IMetadata : IComponent {
    string[] Tags { get; set; }
}

Your 'internal' type:

public class Container : IContainer {
    public Container() {
        Components = new List<IComponent>();
    }

    public IList<IComponent> Components { get; }
}

Your 'wrapper' against external data:

public class ExternalClass {
    public string Whatever { get; set; }
}

public class ExternalContainer : IContainer {
    private readonly List<IComponent> _components;

    public ExternalContainer(ExternalClass @class) {
        _components = new List<IComponent> {new ExternalDocument(@class)};
    }

    public IList<IComponent> Components {
        get { return _components; }
    }
}

public class ExternalDocument : IDocument {
    private readonly ExternalClass _class;

    public ExternalDocument(ExternalClass @class) {
        _class = @class;
    }

    public string Name {
        get { return _class.Whatever; }
        set { _class.Whatever = value; }
    }
}

And a usage example:

public class Demo2 {
    public Demo2() {
        var containers = new List<IContainer> {
            new ExternalContainer(new ExternalClass()),
            new Container()
        };

        foreach (var container in containers) {
            // query container for some components

            var components = container.Components;

            var document = components.OfType<IDocument>().FirstOrDefault(); 
            if (document != null) {
                Console.WriteLine(document.Name);
            }

            var metadata = components.OfType<IMetadata>().FirstOrDefault();
            if (metadata != null) {
                Console.WriteLine(metadata.Tags);
            }
        }
    }
}

Notes

The problem with inheritance is that it is a very rigid approach and generally once you start doing it and at some point you hit a wall and want to revert, it's hard to get out of it.

By working against abstractions things are more flexible and things are decoupled.

Here are two examples that might incite you to change your approach:

Composition over inheritance

Using Components

aybe
  • 15,516
  • 9
  • 57
  • 105
  • you are right, composition (or delegation) is in many cases the way to go, but your type hierarchy introduces an immense complexity to a simple problem. Always KISS (https://en.wikipedia.org/wiki/KISS_principle) until you actually have the requirement to work with different types of objects. – John Smith Feb 24 '17 at 14:39
  • You are 100% right. Something that I tell people about but most of the time forget to do myself – aybe Feb 24 '17 at 15:02
  • 1
    Composition is the way to go. It is both SOLID and testable. Inheritance cause a lot of complications and dependencies – Gravity API Dec 12 '21 at 15:42
  • It is but it doesn't sound obvious to some people at first. – aybe Dec 12 '21 at 22:01