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?

Was it helpful?

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?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top