Reputation: 9638
I'm trying to make a generic wrapper class that can wrap any other class and add extra functionality to it. However at the same time I want to be able to use this wrapper everywhere where I would normally use the wrapped class. Currently I use implicit casting which works OK. But in an ideal world I would like to the wrapper class to have the same exposed methods and fields as the wrapped class, like in this piece of example code:
class Foo
{
public int Bar() { return 5; }
}
class Wrapper<T>
{
private T contents;
public void ExtraFunctionality() { }
public static implicit operator T(Wrapper<T> w) { return w.contents; }
}
Foo f = new Foo();
Wrapper<Foo> w = new Wrapper<Foo>(foo);
int y = w.Bar();
Can I use some ancient witchcraft, reflection, or other trickery to make this possible?
Note: in C++ I would just overload the -> operator to operate on the field contents instead of on the wrapper.
Upvotes: 1
Views: 3747
Reputation: 68710
I wouldn't recommend this, but since you've mentioned witchcraft... you can use dynamic typing with DynamicObject
. The wrapper tries to handle the requested method. If it can't, it forwards the call to the underlying wrapped object:
class DynamicWrapper : DynamicObject
{
private readonly object _contents;
public DynamicWrapper(object obj)
{
_contents = obj;
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
if (binder.Name == "ExtraFunctionality")
{
// extra functionality
return true;
}
var method = _contents.GetType()
.GetRuntimeMethods()
.FirstOrDefault(m => m.Name == binder.Name);
if (method == null)
{
result = null;
return false;
}
result = method.Invoke(_contents, args);
return true;
}
}
Edit:
Nevermind, I just noticed you wanted to use an instance of this everywhere where you would normally use an instance of the wrapped type.
You'd have to change the field/property/variable declaration to dynamic
to use this.
Upvotes: 1
Reputation: 22481
You can generate the type(s) dynamically using the classes in the System.Reflection.Emit namespace. This link can give you a good starting point.
Upvotes: 0