开发者

Can you access the concrete class from the abstract class?

开发者 https://www.devze.com 2023-02-15 17:22 出处:网络
I have a abstract class which represents an immutable value.Like strings, the immutable class has methods which appear to change the inst开发者_StackOverflow社区ance, but really just return a new inst

I have a abstract class which represents an immutable value. Like strings, the immutable class has methods which appear to change the inst开发者_StackOverflow社区ance, 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?

public static T Modify<T>(this T immutable) where T:BaseImmutable{
    //work your magic here
    //return T something
}


You could do this:

abstract class BaseImmutable<T> {
    // ... 
    public T ModifiedButNotReally() { // ... }
}

class DerivedImmutable : BaseImmutable<DerivedImmutable> { // ... }

where I've elided a ton of details.

That said, this does smell kind of bad. Basically, I'm giving you a mechanism to solve your problem, it's just not clear to me that whatever you're modeling is best modeled by the setup that you have.


Jason's approach is probably the best option you have. You'll have to add runtime type test (unsafe cast) to the target type, because the C# type system isn't expressive enough for this, but it will work.

To add some background - what you're asking for is called self types. It is the ability to refer to the type of the current instance in the type declaration. This is available in some languages - for example Scala, but unfortunatelly not in C#. In a fictional C# with self types, you could declare a virtual method like this:

public abstract class BaseImmutable {
  public abstract self GetNewObjectFromOld();
}

A class Foo implementing the method would have to return Foo as the result. To find more information and workarounds, you could try searching for various approaches to emulate self types in C#.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号