Changing type of a field in C# at runtime

1.4k Views Asked by At

I have an existing heirarchy of classes, say something like this:

Business
  - Division
    - ProjectTeam
      - Employee

These classes are instantiated via deserialization.

However, now I need to expose extra fields in the Employee for a particular user of the library i.e. say something like this:

SpecialBusiness (extends Business)
  - Division
    - ProjectTeam
      - SpecialEmployee (extends Employee)
        - Degree

The problem is, I can't just make a class that extends 'Business' because the addition I want to make is to the 'Employee' class.

As I see it I have two options:

  1. Duplicate the heirarchy with 'Special' classes. This means each 'Special' class will have a collection of the original classes and a collection of the new 'Special' classes.

    SpecialBusiness
      - Division AND SpecialDivision (extends Division)
        - ProjectTeam AND SpecialProjectTeam (extends ProjectTeam)
          - Employee AND SpecialEmployee (extends Employee)
            - Degree
    
  2. Somehow retype 'Employee' to 'SpecialEmployee' at runtime for deserialization purposes. Know that I can cast all 'Employee' objects from a SpecialBusiness to 'SpecialEmployee' in the codebase (possibly using helper methods to make it obvious).

Any ideas on how to deal with this problem?

7

There are 7 best solutions below

2
On

You cannot change the type of a field at runtime.

That said, why can't you just extend employee if that's what you want to do? Polymorphism states you can plug an object of a subtype into a field of it's supertype? Why do you need to rewrite the parent object?

1
On

Well, I'm not completely sure if it is suitable in your situation, but have a look at my answer for this question. You can derive your Division and Employee classes from Decorable and derive SpecialDivision and SpecialEmployee from Decorator<Division> and Decorator<Employee>.

3
On

I don't know if it's possible for you to do now, but the whole object structure should use interfaces instead of concrete classes.

4
On

Most serialization frameworks, e.g. XmlSerializer and BinaryFormatter, provide ways to deserialize streams in a custom way, so you can have your updated/new hierarchy and deserialize old streams into it. What kind of serialization framework are you using?

3
On

Why can't you muck with your source for deserialization to instantiate the classes that you want at runtime?

1
On

One possible solution (assumes chosen deserialisation method allows private field serialization):

Use the method 1 from the question (i.e. a separate hierarchy) and store the extended versions of the classes in a private field. After deserialization update the regular public non-extended fields with the extended versions and include a method that returns a cast version of these fields.

SpecialProjectTeam
  - (private) SpecialEmployees
  - (public) Employees
  - (public) GetSpecialEmployees (returns Employees field cast as SpecialEmployees)
0
On

When the XML states the actual objects types as:

  <Employees>
    <Employee>
      <Name>Bob</Name>
    </Employee>
    <Employee xsi:type="SpecialEmployee">
      <Name>Carol</Name>
      <Degree>Chief</Degree>
    </Employee>
  </Employees>

at least when deferring from declared type (here Employee[] Employees) the XMLSerializer well supports polymorphism at deserialization, as it will create a SpecialEmployee with Name="Carol" and Degree="Chief"; given SpecialEmployee is derived from Employee and the XMLSerializer is told that.
This can either be done by XmlInclude attribute or by providing the specialised types when creating the XMLSerializer.