首页 > 解决方案 > Return child type from a method on an abstract class or force two constructors on child with different parameters types

问题描述

I have an abstract class TypedIdValueBase which has subclasses like UserId or ImageId. I don't know how but I would like to have a Parse method (or a second constructor which is taking a string in parameter and not a Guid) and call it like that:

Guid valueG = Guid.NewGuid();

UserId userId1 = UserId.Parse(valueG ); //or new UserId(valueG )
ImageId imageId1 = ImageId.Parse(valueG ); // or new ImageId(valueG )

string valueS = "0f8fad5b-d9cb-469f-a165-70867728950e";

UserId userId2 = UserId.Parse(valueS ); //or new UserId(valueS )
ImageId imageId2 = ImageId.Parse(valueS ); // or new ImageId(valueS )

I would like to force each child class to have that method or a second constructor with a string parameter but I really don't know how to handle it. I know about some "simple" answers like that: Abstract Method That Returns an Instance of Derived Class

Someone already linked my previous question to another answer without reading well what I want to do. Pay attention to the type of the variables or parameters before to link and close it to an answer which is not answering the question. Thank you a lot.

The best I had was something like that:

string value = "0f8fad5b-d9cb-469f-a165-70867728950e";
UserId userId = (UserId)UserId.Parse(value); //or new TypedIdValueBase.Parse<UserId>(value)

Does anyone have an idea on how to do something like that ?

Here is the code without traces of my attempts:

public abstract class TypedIdValueBase : IEquatable<TypedIdValueBase>
{
    public Guid Value { get; private set; }

    protected TypedIdValueBase(Guid value)
    {
        if (value == Guid.Empty)
        {
            throw new InvalidOperationException("Id value cannot be empty!");
        }

        Value = value;
    }
}

public class UserId : TypedIdValueBase
{
    public UserId(Guid value) : base(value)
    {
    }
}

public class ImageId : TypedIdValueBase
{
    public ImageId(Guid value) : base(value)
    {
    }
}

标签: c#.netabstract-classsubclass

解决方案


Good day,

I found a way to have something like I wanted but I'm still not sure if it's a good thing to do.

With the code below I can do:

UserId userId = UserId.Parse(stringValue);
UserId userId2 = new UserId(guidValue);


public class UserId : TypedIdValueBase<UserId>
{
    public UserId(Guid value) : base(value)
    {
    }
}

public abstract class TypedIdValueBase<T> : TypedIdValueBase where T : TypedIdValueBase
{
    public static T Parse(string value)
    {
        var obj = (T)new object();
        obj.Value = Guid.Parse(value);
        return obj;
    }

    protected TypedIdValueBase(Guid value) : base(value)
    {
    }
}

public abstract class TypedIdValueBase : IEquatable<TypedIdValueBase>
{
    private Guid _value;

    public Guid Value
    {
        get => _value;
        protected internal set
        {
            CheckValue(value);
            _value = value;
        }
    }

    protected TypedIdValueBase(Guid value)
    {
        CheckValue(Value);
        Value = value;
    }

    private void CheckValue(Guid value)
    {
        if (value == Guid.Empty)
        {
            throw new InvalidOperationException("Id value cannot be empty!");
        }
    }

    public bool Equals(TypedIdValueBase other) => Value == other?.Value;

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj))
        {
            return false;
        }

        return obj is TypedIdValueBase other && Equals(other);
    }

    public override int GetHashCode() => Value.GetHashCode();

    public static bool operator ==(TypedIdValueBase obj1, TypedIdValueBase obj2)
    {
        if (Equals(obj1, null))
        {
            if (Equals(obj2, null))
            {
                return true;
            }

            return false;
        }

        return obj1.Equals(obj2);
    }

    public static bool operator !=(TypedIdValueBase x, TypedIdValueBase y) => !(x == y);
}

推荐阅读