I have a abstract class which represents an immutable value. Like strings, the immutable class has methods which appear to change the instance, but really just return a new instance with the changes. The abstract class has some of these methods which have some default behavior. Can I have this kind of Create modified instance and return logic in the abstract class? I’m getting hung up because I don’t know what to put in the ??? positions. It seems like what I need is a type T that represents the deriving type. Are there any such patterns out there to overcome this problem?
public abstract class BaseImmutable
{
public readonly object Value;
protected BaseImmutable(object val)
{
Value = val;
}
public ??? ModifiyButNotReally()
{
object newValue = GetNewObjectFromOld(Value);
return new ???(newValue);
}
}
public class DerivedImmutable : BaseImmutable
{
public DerivedImmutable(object val) : base(val)
{
}
}
ANSWER
This was easier than I suspected. A method can be made generic without making the class generic. With that in mind the only thing we have to do is constrain the generic argument to inherit from the base class and require it not be abstract by including a new() constraint. One last step I needed to do was to use reflection to get the non-parameterless constructor to create a new instance. I found this solution to be better than the generic class answer suggested by @Jason, which I have since learned is known as the Curiously Recurring Template Pattern. It also avoids the need to create a difficult-to-test/moq extension method in a separate static class as suggested by @bottleneck. However, @bottleneck’s answer is what gave me the inspiration for the solution so the points go to him.
public abstract class BaseImmutable
{
public readonly object Value;
protected BaseImmutable(object val)
{
Value = val;
}
public T ModifiyButNotReally<T>() where T : BaseImmutable, new()
{
object newValue = GetNewObjectFromOld(Value);
var ctor = typeof(T).GetConstructor(new[] { typeof(object) });
return (T)ctor.Invoke(new object[] { newValue });
}
}
public class DerivedImmutable : BaseImmutable
{
public DerivedImmutable(object val) : base(val)
{
}
}
Maybe an extension method would be in order here?