Question
On C# 3.0 and .NET 3.5, imagine there's an interface:
public interface INameable
{
string Name {get;}
}
and many immutable classes that implement the interface.
I would like to have a single extension method
public static T Rename<T>(this T obj) where T : INameable
{
...
}
that returns a wrapped instance of the original object with just the name changed and all other property reads and method calls routed to the original object.
How to get a generic wrapper class for this, without implementing it for all INameable implementing types? Do you think that's possible?
Solution
No, this isn't possible, unless T
is constrained to be an interface or to be a class with all members virtual, and this constraint isn't possible to specify at compile time (though you could write a runtime check if you're happy with that approach).
The reason you cannot do it for arbitrary types is that if you take an object of type T
then you must return an object that is assignable to T
. If I pass an object that looks as follows...
public sealed class SomeClass : INameable
{
public string Name { get; }
public string Abc { get; }
}
...then there is no way you can create another type that is assignable to SomeClass
. Not using Reflection.Emit
or any other method, because the type is sealed.
However, if you're happy with the restrictions that I've mentioned then you could use something like the Castle DynamicProxy framework to create a type that proxies the passed in object and intercepts the calls to either forward or re-implement as appropriate.
OTHER TIPS
Well, yes, it's possible, but it involves generating code in memory.
You can look at Reflection.Emit and here.
Note that it will involve a lot of code.
That is, if I assume I understand you correctly.
Here's what I think you're asking for:
SomeNameableObject a1 = new SomeNameableObject("ThisIsTheFirstName");
SomeNameableObject a2 = a1.Rename("ThisIsTheSecondName");
// a1 works, still has the name ThisIsTheFirstName
// a2 works, but everything is routed to a1,
// except for the name, which is ThisIsTheSecondName
Is that correct?