简体   繁体   中英

C# cast polymorphic object into specific type without knowing what that 'specific type' is

Thanks ahead, community!

As the title describes, I would like to cast an object that is in parent type to a child type, which is actually a child type, whilst this 'specific type' cannot be known until runtime.

Lets say I have following data holder:

public class Holder {}

public class Holder<T> : Holder
{
    public T Value;
}

And this Holder (not Holder<T>) will be given to some script at runtime.

I need to cast this Holder into Holder<T> (eg, Holder<string>), so that I can access the Value: T.

For now, I can just mannually add casting cases and their coresponding methods to process it, but time by time there will be more types that goes into this Holder<T>, and it would become imposible to manage in this way.

Is there a way to accomplish this objective?

This Holder must not be flattened, as it is being used in a context as below:

public class SomeNode
{
    protected Holder holder;
}
public class SomeNode<T> : SomeNode
{
    public SomeNode<T>()
    {
        holder = new Holder<T>();
    }
}

I have no clue how to approach this, nor a search keyword to catch a hint about this. Automatic suggestions came up before posting seems not my case, which were:

C# Create (or cast) objects of a specific type at runtime

C# Accessing generic Method without knowing specific type

community. It's a good question. That was interesting. I think this is simple solve for this question. We just need to create a simple constructor like below


public class Holder
{
    public string SomeData;  // just example data

    public Holder()
    {
        
    }
    
    public Holder(Holder someData)
    {
        SomeData = someData.SomeData;
    }
}

public class Holder<T> : Holder
{
    public T Value;

    public Holder(Holder a, T t = default)
        :base(a)
    {
        Value = t;
    }
}

public class Programm
{
    void Main()
    {
        var h = new Holder();
        var g = new Holder<string>(h);
    }
    
}

If you need to cast to a specific type, you are doing polymorphism wrong. Of course you could do something like this:

switch (holder)
{
    case Holder<string> stringHolder:
        DoStringThing(stringHolder.Value);
        break;
    case Holder<int> intHolder:
        DoIntThing(intHolder.Value);
        break;
    ...
}

See also: Switch statements with patterns .

However, the idea behind polymorphism is to be able to do things without having to know the specific type. Therefore, re-design the holder classes and have them do the type specific thing themselves:

public abstract class Holder
{
    public abstract void DoThing();
}

public abstract class Holder<T> : Holder
{
    public abstract T Value { get; }
}

Some examples of specific types:

public class StringHolder : Holder<string>
{
    public StringHolder(string value)
    {
        Value = value;
    }

    public override string Value { get; }

    public override void DoThing()
    {
        Console.WriteLine($"String of length {Value.Length} is \"{Value}\"");
    }
}

public class IntHolder : Holder<int>
{
    public IntHolder(int value)
    {
        Value = value;
    }

    public override int Value { get; }

    public override void DoThing()
    {
        Console.WriteLine($"The integer {Value} is {(Value % 2 == 0 ? "even", "odd")}");
    }
}

Now you can simply write

holder.DoThing();

... without having to cast.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM