Lecture thumbnail 0:00 / 0:00 So in our discussion of the explicit ID Copyable interface, the one thing that’s missing out or one

of the many things that’s missing out is inheritance.

How do we actually manage inheritance?

What happens if we’ve got a person class, which is ID, Copyable and then somebody inherits from that

class?

How is it going to work?

Well, in actual fact, you can get it to work, but it’s not going to be pretty.

Just like all the examples of inheritance that we see when we have to turn to things like recursive

generics and stuff.

It’s going to get a bit messy here, but I’m going to show you how this can all work.

So I am recreating this example from scratch because it’s going to be sufficiently different from what

we’ve already done.

So I’m going to first of all, have a class called address.

So address is going to have public string, street name and public int house number.

And you’ll notice that what I’m going to do is I’m going to have a constructor which initializes the

address, but I’m also going to have a default constructor.

So I’m going to have a constructor which just doesn’t assign anything at all.

Let’s get some more members in here so that we can subsequently print them out, get the formatting

members just to kind of a two string implementation that we can subsequently use.

So this is our address and now I’m going to continue constructing this example.

So we’re going to have a class called person.

So person is going to have a bunch of names.

Let’s say we have an array of names, maybe first name, last name, middle name, if that’s applicable.

And we’ll also have a public field for address like so.

Okay, So once again, we can have a fully initializing constructor.

That’s fine, but we can also have a constructor that doesn’t really do anything.

So an empty constructor.

Once again, I will provide some sort of two string implementation.

So let me just do that quickly.

Now, of course the ID isn’t smart enough to know that this array should probably be printed something

like string dot join comma names, something like that.

So that’s just to make it pretty so that we can subsequently read this whole thing.

And now let’s imagine that we have an inheritance scenario.

So in addition to the class person, we also have an inheritor, we have a class called employee.

Employee is going to be a person.

So we’re going to inherit.

And what we’re going to be adding is just a single integer field called salary.

So here is the integer field.

Now, once again, what you can do is you can both generate a constructor which initializes just about

everything that there is there to initialize.

And we’ll also have an empty constructor as well.

Just an ordinary empty constructor like so let’s also implement two string just so that we get to sort

of print this when the time comes.

Actually, for some reason it just printed to a string here without any kind of extra information.

This is actually a problem.

Why?

I don’t understand why it didn’t show.

Oh, oh, wait.

Base dot two string.

Yeah, that’s going to obviously print all the base class stuff.

Okay, So we’ve got our scenario.

So we’ve got person which uses an address and an address is right here.

It’s a separate class.

And then we have employee which inherits from person.

And what we want is we want some sort of deep copying interface.

So once again, what we want is some kind of interface called ID copy Copyable of T, obviously where

T is the class that we’re copying.

Now, you’ll notice that this time around I’m not saying our T, so I’m not, I’m not at liberty to

add the out keyword here.

You’ll see why in a moment because this interface is going to be very different to what we have done

previously.

But for now, let’s imagine that we’re still working with the old scenario, the one that you’ve already

seen, and under this scenario you just have a method called deep copy, which returns a value of type

T, And what you could do, of course, is you could go ahead and implement this as is.

So here I would say this is ID copyable of address and I would implement it like so now here the, the

actual implementation because both of these are trivially copyable types, we can just do a clone equivalent

so we can return a memberwise clone like this.

Obviously you have to cast it because it’s not strongly typed so we can cast it to an address.

So that is the implementation for address.

Now for person, let’s also implement ID copy both for person like this.

So implement the missing members.

So here what we would have to do is we would make maybe a new person where oops, where we would replicate

some of the bits.

So the first thing is an array of names, but of course it’s an array of trivially copyable types.

So we can as always, say something like Names dot clone.

That would be completely valid because obviously it’s a shallow copy, but because the types are trivially

copyable, this means we can just, you know, make a new array and stick the same old references in

there because strings have value semantics.

So it’s not like something is going to go wrong here.

And then of course, the second argument is the address.

Now address has its own deep copy mechanics.

So here we say address dot deep copy, and that’s pretty much it.

Don’t forget that if you call clone, you have to cast it because unfortunately there is an cloneable

but there is no cloneable of T, and that means every time you use clone you have to cast it because

it returns an object, not a strongly typed return value.

Okay, so this is the implementation for person and then for the employee.

Once again we have ID copyable of employee, we implement the interface and here we return a new employee.

And remember, I’ve generated these fully initializing constructors just so I can show you how this

works.

So here we would take the names.

Of course, that would be once again names Dot Clone.

The address has its own Deepcopy implementation.

So we would say address dot deepcopy and the salary can just go by value.

And let’s not forget the cast as always, and we are done.

So this demonstration is actually very simple so far in that we’ve got something that actually works

and I can show you how it works so we can make a person, let’s say John is a new employee and then

we can initialize John with some values.

So John might have a bunch of names.

So John Doe, for example.

John might also have an address.

Let’s have a new address where let’s have it like this house number equals 123 and let’s say street

name equals London Road like so.

So John has an address.

John also has a salary of, let’s say, 321,000 of whatever currency.

So now if you wanted to make a deep copy of John, you would say var copy equals John deep copy Like,

So this would work just fine.

And just to show you that some things can be changed and they don’t modify the original, Let’s say,

let’s take the copy and let’s change the last name to Smith.

And in addition, let’s change the house number.

Copy that address, dot house number.

I’ll just do a plus plus here and then copy dot salary.

Let’s change it.

Let’s lower it a bit to 123,000.

So now we’ve got a copy and we can print line both John as well as the copy.

So now let’s take a look at what we actually get as we run all of this.

Hopefully we get the right things.

Dum dum.

So as you can see, everything is fine.

We have John Doe and we have John Smith.

They have different salaries, they have different house numbers 123 and 124 So they’re obviously distinct

objects, so that’s good.

But there is a problem with this entire scenario.

And the problem is that I’ve created these fully initialized constructors so that the derive constructor

calls, the base constructor, as you can see right here.

And if you imagine a reasonably large inheritance hierarchy where you want to clone by calling the constructor,

that means that at every point the inheritance has to have a constructor which initializes both the

parent as well as the children, which is really painful.

Now of course you can replace this constructor with just a bunch of assignments, but the problem is

this whole approach does not scale because what we’re doing here, this part is a repetition of what

happens in the constructor right here or in the deep copy method, rather.

So you’ll notice that this code is very similar to this code.

In fact, it’s identical.

So the question is how can we cut down on the number of repetitions?

How can we make sure that each element of the inheritance hierarchy only initializes its own part?

Now, this is going to be a demo with a bit of overengineering, shall we say.

But on the other hand, you will see how flexible C-sharp is in being able to figure this whole thing

out.

So we’re going to modify the IDE Copyable interface, we’re going to change it, and we’re going to

put in a few more requirements.

The first requirement that we’ll put in is that TI has to implement a default constructor because remember,

if we want to build elements piece by piece, then we have to start somewhere.

We have to start with a blank slate.

So this is the blank slate requirement.

Unfortunately, you can’t go without this.

Okay, so now we’re going to change the interface members here.

So first of all, instead of having a deep copy method or we’ll have one, but first of all, we’ll

have a method called copy to which takes a target.

So the idea is that every single class in the inheritance hierarchy has to be able to copy its own internal

state into some target.

So this much is obvious, but the next part is going to be really weird.

I’m going to define a method called Deep copy, but I’m going to give it a body because in C sharp you

can give an interface, a default interface member.

So here I’m going to say the following.

I’ll say T equals new T, so I’ll make an instance of T, then I’ll call the copy to method on T, and

then I will return T.

Okay.

So what’s going on here?

Well, we now have a default interface member, which means that every single class that we have, which

implements ID Copyable is going to have this member.

But the only way you’d be able to call this member is by explicitly casting a variable to this particular

interface, because that’s how default implementations work for interfaces.

That’s kind of a requirement.

So now we need to make a few changes here because now obviously the the approach that we are adopting

here is not the not the right approach.

So let’s go ahead and let’s generate the code for the missing members.

So let’s get rid of this deep copy right here.

We no longer need it.

And now we have this member called Copy too.

So we need to be able to copy an address into a different address.

Now, in our case, it’s very simple, but we no longer have the ability to just use member wise clone.

Unfortunately, we have to do this by hand.

So I have to say something like Target Dot Street name equals street name and target dot house number

equals house number.

But that’s it.

That’s all that we have to do for address now, for person and for employee, it’s going to be a bit

more complicated.

So once again, let’s go here and let’s implement the missing members.

I’m going to get rid of the deep copy that we had previously.

We no longer have to have this member because we get it from the interface.

So we need to copy person into a different person.

So we need to first of all copy the names.

So target dot names equals and then casting to a string array.

We take names, dot clone like so, and for the address target dot address equals address dot deep copy.

Now this is broken for now, so this part isn’t going to work just yet and we’ll get it to work in a

moment.

But let me just explain why it doesn’t work.

The reason why it doesn’t work is because even though we are trying to call deep copy on this particular

object, which is address, its actual type is address and if you want to.

Be something.

Using this default interface implementation, you need to cast the object into an copyable of something.

So of course what we could do here is we could take this address and we could say ID copyable of address,

address, you know, deep copy, we could do this.

This is legal code, but we’re not going to.

Instead, we’re just going to undo all of this and keep it like this for now.

And we’ll come back to this particular point in just a moment.

So let’s imagine that this already works and we’ll jump into employee and now employee has to have this

functionality.

So once again, implement the missing members, get rid of the deep copy implementation that we had

previously and now we need to basically perform the copying.

Now here is the interesting part because this is where in addition to copying the salary, we also want

to call the implementation from the base class.

And this is something that we do using the base keyword.

So here you would say base and after base, what you would do is you would say copy to and you would

copy to the target, but you would copy from the base class.

And then we say target dot salary equals salary.

There we go.

Okay, So we are almost okay here, except we’re not, because there is a problem with at least this

part.

Okay, so how can we actually get this default implementation to work without having to cast it to the

interface?

The only way you can do this in C-sharp at the moment is using extension methods.

So that’s exactly what we’re going to do.

We’re going to define a class called extension methods.

It’s going to be a static class, obviously, and we’re going to define a copy too, but we’re going

to do it in a very slight way.

We’re going to do it generically.

So I’m going to have a public static t returning method called deep copy.

Deep copy, which takes an ID copy, all of T, let’s call it item and all it does.

And by the way, yes, you have to have that constraint that the new constraint, unfortunately is going

to be everywhere.

Now you add it to the interface and so it has to be everywhere.

So you simply return item dot deep copy.

Okay.

So it might look like we are doing something mad, like this is some sort of deep recursive call because

this deep copy obviously calls this deep copy, blah blah blah.

But in actual fact, it’s not an actual fact.

If you study this, you’ll see that this deep copy call actually takes us here.

So this calls the default implementation.

That’s what we want.

And now all of a sudden the consequence of this is that this line is no longer broken.

It’s now a completely legitimate line, because essentially this call isn’t just a call on the interface,

but it’s a call on the extension method, which calls the default interface member.

So that’s good.

But the code is still slightly broken.

If we scroll down to this line, you’ll see that this line does not work.

Now this line doesn’t work for a different reason.

The reason why it doesn’t work is because essentially what we have is we have an ability to deep copy

John, which is an employee, not just into employee, but in actual fact we can sort of clone it partially

into any parent class of employee, which is amazing.

So for example, I can clone it to an employee.

Obviously I can say var equals John dot deep copy into employee like so.

So this is legal.

But also what I can do is I can take it and clone it into a person.

And this is also legal.

This basically takes John, just the parts of John which are relevant to person and it constructs a

new variable.

So the variable type here would be person, and the variable type here will obviously be employee.

So these two things are legal on the one hand.

On the other hand, maybe we want to avoid them.

Maybe if we know that John is an employee, maybe we should be able to just call deep copy without this

extra generic part.

So that would be nice.

Now the only way you can do this is to define yet another extension method.

Unfortunately, there is no way around it.

We’re going to have another extension method.

This one is going to be public static t deep copy t this t person.

So notice this time around we have an explicit allusion to the fact that this is a person and in actual

fact, we’re going to specify it.

We’re going to say that T is a person and we also have to have that new constraint.

Unfortunately, it’s everywhere.

Once we define it up in the interface, now it’s just all over the place really.

So here we have to do an explicit cast, so we have to take person and we have to explicitly cast person

into an ID copyable.

So that’s exactly what we do.

So we take ID copyable of T, we cast person into this variable and then we call deep copy on this variable.

So now our demo is complete.

Now we can run this once again and hopefully we get the same results as we did when we called those

constructors explicitly.

So let’s run all of this and let’s take a look at what we get.

And as you can see, everything works just fine.

We have John Doe with House number 123, salary 321,000.

And then we have John Smith house number 124 with 123,000.

So what is the takeaway from this demo?

Well, the takeaway is that if you are happy with just ordinary constructors, just fully initializing

constructors all over the place, like here and here and here, then you don’t have to do all of this

magic.

You can just keep calling those constructors.

But eventually, if your inheritance hierarchy is big enough, you won’t like it.

You won’t like having constructors which take ten parameters, so you’ll switch to using parameter objects

or something to that effect.

And then of course you will have to have your own copying methods where you assign all of those and

it will.

Be a lot of repetition.

There will be a lot of similar assignments.

They are not complicated.

They’re not taxing to the intelligence, but there’s going to be too many of them.

So in this case, even though we’re sort of overengineering things, what we’re doing is we’re saving

some energy because we are reusing base class functionality right here.

So the approach I’ve shown is only one of a number of approaches.

Another approach would be to use recursive generics, which is also possible, and maybe it could produce

a neater solution.

But recursive generics are painful because they introduce a lot of type information right into the inheritance

type arguments.

And if we can avoid recursive generics, at least for this demo, then I prefer to avoid them.

So that’s my demo of inheritance when it comes to deep copying.

Play Play Play Play Stop Play Start Play