Home > Software engineering >  C# store different child classes and call them at same time
C# store different child classes and call them at same time

Time:01-05

Actually all these classes are defined in the 3rd lib, so I can't change them.

=====================

I'm learning C# and I met a problem. Suppose I have a parent class and two child classes:

class ParentClass
{
    ....
};

class ChildA : ParentClass
{
    public string name;
};

class ChildB : ParentClass
{
    public string name;
};

Both class ChildA and ChildB have property name, but ParentClass doesn't. Now I need to store ChildA and ChildB in a dictionary, so I write Dictionary<string, ParentClass>.

But I can't get name because ParentClass doesn't have this property:

foreach (ParentClass pc in dict.Values) 
{
    // it works, but too verbose as I may have ChildC, ChildD...
    if (pc is ChildA ca) 
    {
        ca.name
    }
    if (pc is ChildB cb) 
    {
        cb.name
    }

    // how can I get the name property at same time?
}

how can I handle this?

CodePudding user response:

The short version is "no". There are things that you could do if you have access to the types - for example, you could implement a common interface (interface IHazName { public string Name {get;} }) - but you can't do that here since you don't control the types.

A lazy way could be to abuse dynamic:

dynamic hack = pc;
hack.name = "yolo";

but... please don't! Your is approach (or perhaps a switch expression) is about as good as you can get. Note that if you need to talk to this member in a lot of places, you could move that shared logic to an extension method:

static class SomeUtilsType {
    public static string GetName(this ParentClass obj) => obj switch {
        ChildA ca => ca.name,
        ChildB cb => cb.name,
        _ => throw new ArgumentException("Unexpected object type", nameof(obj)),
    };
}
...
foreach (ParentClass pc in dict.Values) 
{
    Console.WriteLine(pc.GetName());
}

(or a similar set method) - then at least you don't need to repeat yourself.

CodePudding user response:

This seems kind of hacky to me, but you could use the dynamic type to get the name property. Basically dynamic will prevent compile errors, but then you have to guard against runtime errors using something like a try/catch block.

For example:

public static string GetName(ParentClass input)
{
    dynamic obj = input;

    try
    {
        // If the type passed in has a "name" property, this will work
        return obj.name;
    }
    catch
    {
        // If the type didn't have a "name" property, a RuntimeBinderException 
        // is thrown, so we return a default value here
        return null;
    }
}

Example usage:

static void Main(string[] args)
{
    ParentClass parent = new ParentClass();
    ChildA child = new ChildA { name = "ChildName" };

    Console.WriteLine($"Parent name: {GetName(parent)}");
    Console.WriteLine($"Child name: {GetName(child)}");

    Console.Write("\n\nDone. Press any key to exit...");
    Console.ReadKey();
}

Output

Parent name:
Child name: ChildName


Done. Press any key to exit...
  • Related