views:

53

answers:

4

Is it possible to do something like the following:

public class ChildClass : BaseClass
{
    public ChildClass(BaseClass o)
    {
        base = o;
    }
}

Basically, I want a transparent way to wrap a base class inside of other functionality. One example I've thought of is a custom Settings Provider which transparently audits the settings passed through it.

public class SettingsAuditor : SettingsProvider
{
    public SettingsAuditor(SettingsProvider o)
    {
        base = o;
    }

    public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection propvals)
    {
        // Log the property change to a file
        base.SetPropertyValues(context, propvals);
    }
}

Then I could do the following:

mySettingsProvider = new SettingsAuditor(mySettingsProvider);

And all changes would go through the overridden SetPropertyValues before passing to the original object.

I could use a private SettingsProvider member, but then I either cannot inherit from SettingsProvider, or have an entire SettingsProvider (base) not being used at all.

I'm using C# 4.0 and .Net 4.0.

+2  A: 

No. This looks like it should be a Composition vs Inheritance issue. You need to evaluate whether you are a "is a" or a "has a."

A little help for your journey

Nix
I understand that `SettingsAuditor` technically "has a" `SettingsProvider`, but I feel like it should be transparent to the developer, an should be used like a `SettingsProvider` itself. Another issue is sockets - often, classes I write "have a" `TcpSocket`, but they expose all the same methods, but with extra logic, and act "as a" `TcpSocket` themselves.
Daniel Rasmussen
+1 for the distinction, though, and the link.
Daniel Rasmussen
A: 

No, but you could fake it:

public class SettingsAuditor 
{ 
    SettingsProvider @base;

    public SettingsAuditor(SettingsProvider o) 
    { 
        @base = o; 
    } 

    public void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection propvals) 
    { 
        // Log the property change to a file 
        @base.SetPropertyValues(context, propvals); 
    } 
} 

Note here, @base isn't the actual base, just a varaible named base

James Curran
Again, `mySettingsProvider = new SettingsAuditor(mySettingsProvider);` wouldn't work with this solution.
Daniel Rasmussen
+3  A: 
  1. You cannot do base = o;
  2. What you're looking for is the Decorator Pattern), which is a way to compositionally add functionality at runtime (vs. inheritance).

Instead of trying to set the base, you just contain the inner member. As long as the wrapper implements the same interface or base class as the inner object, you can pass back the new wrapper. You can wrap as many decorators as you want.

Consider:

public interface ICar
{
    void Drive();
}

public class Car : ICar
{
    public void Drive()
    {
        Console.WriteLine("vroom");
    }
}

public class BuckleUp : ICar
{
    ICar car;

    public BuckleUp(ICar car) { this.car = car; }

    public void Drive()
    {
        Console.WriteLine("click!");
        car.Drive();
    }
}

public class CheckMirrors : ICar
{
    ICar car;
    public CheckMirrors(ICar car) { this.car = car; }

    public void Drive()
    {
        Console.WriteLine("mirrors adjusted");
        car.Drive();
    }
}

Now consider you have a method that accepts an ICar and tells it to drive. You could give it a Car, and it would work, but you could also wrap that car in a BuckleUp and a CheckMirrors and you wouldn't have to change that method at all. You've modified functionality through composition using the Decorator Pattern.

Jay
I like this pattern, but it's not entirely possible for base classes I don't control (like `SettingsProvider` and `TcpSocket`.) The only thing I could inherit is the class themselves, and then I'm stuck with a brand new base class every time, and can't wrap around an existing object.
Daniel Rasmussen
@Daniel True, in some cases it won't work, as with sealed classes. Typically, though, you can just redirect all calls to the decorated object. For example, if the `ICar` interface had a `int Mileage` property, then `BuckleUp` and `CheckMirrors` would each implement the property like this: `get { return car.Mileage; } set { car.Mileage = value; }` You can do the same thing with any non-sealed class, but you might have to declare the members as `new` if the base class members are not declared `virtual`.
Jay
A: 

This is not a complete implmentation and it could probably be done much cleaner with expression trees... but this was a quick swing at faking AOP using DynamicObject with .Net 4.0.

public class MyDynamicWrapper<T> : DynamicObject
{
    public T Wrapped { get; private set; }
    public Action<T> Pre { get; private set; }
    public Action<T> Post { get; private set; }


    public MyDynamicWrapper(T wrapped, Action<T> pre, Action<T> post)
    {
        this.Wrapped = wrapped;
        this.Pre = pre;
        this.Post = post;
    }

    public override bool TryGetMember(
        GetMemberBinder binder, 
        out object result)
    {
        var type = typeof(T);
        var method = type.GetMethod(binder.Name);
        if (method != null)
        {
            Func<object> func = () =>
            {
                if (Pre != null)
                    Pre(Wrapped);

                // support for input parameters could be added here
                var ret = method.Invoke(Wrapped, null);

                if (Post != null)
                    Post(Wrapped);
                return ret;
            };
            result = func;
            return true;
        }

        return base.TryGetMember(binder, out result);
    }
}
public class MyDynamicWrapper
{
    public static MyDynamicWrapper<T> Create<T>(
        T toWrap, 
        Action<T> pre = null, 
        Action<T> post = null)
    {
        return new MyDynamicWrapper<T>(toWrap, pre, post);
    }
}

public class MyObject
{
    public void MyMethod()
    {
        Console.WriteLine("Do Something");
    }
}

class Program
{
    static void Main()
    {
        var myobject = new MyObject();
        dynamic mydyn = MyDynamicWrapper.Create(
                                myobject, 
                                p => Console.WriteLine("before"), 
                                p => Console.WriteLine("after"));
        // Note that you have no intellisence... 
        // but you could use the old implmentation before you 
        //   changed to this wrapped version.
        mydyn.MyMethod();

        /* output below
        before
        Do Something
        after
        */
    }
}
Matthew Whited
Right now this does not support parameters being provided to the method. But that support could be added pretty easily. Also if this was changed to expression trees you should be able to have better type and method resolution.
Matthew Whited