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...