9

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

  }           

}
Israel Lot
  • 653
  • 1
  • 9
  • 16
  • 3
    Make both conform to an interface, IEmployee? – SpaceBison Apr 16 '12 at 10:55
  • I can't modify Employee. – Israel Lot Apr 16 '12 at 10:57
  • @IsraelLot, you certainly can downcast in C#. I suspect you are just missing the syntax: `AdvancedEmployee advanced = (AdvancedEmployee)employee;` Of course this will give a run-time error if the referenced object is not in fact an instance of AdvancedEmployee. – Ben Apr 16 '12 at 12:51
  • @Ben: in Israel's example, employee is cleary not an instance of AdvancedEmployee, but just an Employee. – comecme Apr 16 '12 at 15:14
  • @comecme, yes I was lead astay by his use of the word "downcast". He isn't down-casting at all, he is converting to a derived type. – Ben Apr 16 '12 at 15:26

5 Answers5

6

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.

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
Tigran
  • 61,654
  • 8
  • 86
  • 123
5

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 { } 
Ben
  • 34,935
  • 6
  • 74
  • 113
Torbjörn Kalin
  • 1,976
  • 1
  • 22
  • 31
  • The downvote is my fault. I misread the question. Unfortunately some misfeature means I cannot revert my vote now I realise I was mistaken. – Ben Apr 16 '12 at 14:27
  • 1
    Turns out if you edit the post (even adding a single space) you can then revert your vote. – Ben Apr 16 '12 at 14:55
3

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));
John Fairbanks
  • 1,342
  • 12
  • 16
2

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;
tukaef
  • 9,074
  • 4
  • 29
  • 45
  • Since the OP can't modify `Employee`, he also can't define explicit or implicit type-conversion operators inside it. – comecme Apr 16 '12 at 15:20
1

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.

Vlad
  • 35,022
  • 6
  • 77
  • 199
  • That's what I'm doing right now. I was wondering if there was a fancy way. – Israel Lot Apr 16 '12 at 11:01
  • 2
    Not really. There are another OO languages where you can change the _runtime type_ of an object on the fly -- but not in C#/Java/C++. in C#, you can only create a _new_ object. – Vlad Apr 16 '12 at 11:03
  • Looks to me like the questioner just wants to know how to do a downcast in C#. – Ben Apr 16 '12 at 14:00
  • @Ben: not really. Look, the OP creates an instance of `Employee`, and tries to make it into an `AdvancedEmployee`. He says that he accomplishes it with a constructor taking `Employee`, so the problem is really in changing the runtime type. – Vlad Apr 16 '12 at 14:03
  • @Vlad unfortunately some misfeature means I cannot now revert my vote. – Ben Apr 16 '12 at 14:24
  • @Vlad turns out if you edit the post you can revert your vote. What is the point of that? Oh well.... – Ben Apr 16 '12 at 14:54