简体   繁体   中英

Cast base instance to derived class (downcast) in C#

Suppose I have two classes:

class Employee 

and

class AdvancedEmployee:Employee

I know something like this won't work, as I can't downcast on C#:

var employee = new Employee();
var advanced = employee as AdvancedEmployee;

My question is: How to accomplish downcast in a efficient way? Actually I have a constructor on AdvancedEmployee that takes a Employee as parameter and use it to inject its values, basically making a clone.


Update

To solve the data that would get duplicated I changed the approach a bit and now AdvancedEmployee CONTAINS an employee rather than being one itself. Example:

class Employee;

class AdvancedEmployee
{
   private employee

   public AdvancedEmployee(Employee employee){

    this.employee = employee

  }           

}

Create an interface. And since you can't modify Employee, create an adapter that you own:

class EmployeeAdapter : IEmployee
{
    private Employee emp;
    public EmployeeAdapter(Employee emp) { this.emp = emp; }
    public int SomeMethodInEmployee() { return emp.SomeMethodInEmployee(); }
}

class AdvancedEmployee : IEmployee { } 

It can not be a cast , it's actually a conversion between different types.

I would add a ctor or static member function like AdvancedEmployee FromBase(Employee e) , to construct derived type from given base type.

Here is a way I have handled it in the past which i thought was kinda cool... if all the data elements in Employee and AdvancedEmployee are Properties, then you can use reflection to copy the data values from the base class into the derived class. Then you can work with the derived class as if he was originally typed that way without having to encapsulate the base class.

public class Employee
{
    public int ID { get; set; }
    public string Foo { get; set; }

    public void SomeMethod() { }
}

public class AdvancedEmployee : Employee
{
    public string Bar { get; set; }

    public void SomeAdvMethod() { }
}

Employee emp = new Employee() { ID = 5, Foo = "Hello" };

// To create a AdvancedEmployee from emp
AdvancedEmployee advEmp = new AdvancedEmployee() { Bar = "A prop not in Emp that might need a value" }
foreach (var p in typeof(Employee).GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.CanWrite))
    typeof(AdvancedEmployee).GetProperty(p.Name).SetValue(advEmp, p.GetValue(emp));

Creating a new object is the most readable and clearest way. You can also define explicit or implicit type-conversion operators in your Employee class.

public static explicit operator AdvancedEmployee(Employee e)
{
    return new AdvancedEmployee(e);
}

And then use it like this:

var e = new Employee();
var ae = (AdvancedEmployee) e;

as in C# checks for the runtime type . That is, if the object being tested is indeed not an AdvancedEmployee , you'll not make it magically convert Employee to AdvancedEmployee . So if you need an AdvancedEmployee , you need to construct it yourself somehow.

There are different approaches depending on what you are going to achieve. Perhaps you can do with a constructor AdvancedEmployee(Employee proto) , which will copy the needed values from the proto ? Or maybe you need to wrap the existing Employee with an AdvancedEmployee .

Note that you might need to let the code which stores the old employee replace it with your newly-created AdvancedEmployee .

A way to go (with some Java taste) might be to create an Employee factory, which would create an AdvancedEmployee if needed. Then you need to make all the code use the factory instead of creating an Employee with new . This scenario would however not help if you anyway need changing your runtime type.

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