Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question about Assign chapter #4

Closed
eugeneloza opened this issue Feb 20, 2017 · 3 comments
Closed

Question about Assign chapter #4

eugeneloza opened this issue Feb 20, 2017 · 3 comments

Comments

@eugeneloza
Copy link
Contributor

Hi, Michalis!
I've got a problem understanding the following line:

Be careful when calling inherited in the Assign implementation. You should call inherited TPersistent.Assign only if you cannot handle the assignment in your code (this allows the AssignTo method to work, or otherwise raise an exception when assignment cannot be done).

Could you please explain it more specificly...
What's bad about using inherited Assign method? It doesn't copy all the fields? Or only those published?
What are the cases when one cannot handle the assignment in his code? Looks quiet straightforward to me... Of course, something more automatic (that won't require me to keep in mind to add each new field to Assign method) would be better, but anyway?
What "this" exactly allows AssignTo method to work - TPersistent.Assign or manual Assign implementation?
What sort of exception is risen? Or is it just a possibility to raise a manual exception in case the assignment cannot be done (by using some manual internal checks)?
Sorry for, maybe, very stupid question :) I've never used Assign before, so this might be something very basic...
Actually I don't really need exact answers to these questions (I'm almost sure, I'll figure it out, when I'll make something like this in practice), but the fact that I don't understand them makes me unsure whether I've translated those sentences correctly :)

@michaliskambi
Copy link
Owner

I didn't place that sentence ("Be careful when calling inherited...") in a proper context. These are good questions, and I will update the documentation to answer them. Te key is to know how the TPersistent is implemented.

My intention was to say this:

  • Do not call "inherited" (when overriding Assign) in a direct (immediate) ancestor of TPersistent.
  • Otherwise, do call "inherited" when overriding Assign. (And, in general, calling "inherited" in overridden methods is usually a good idea.)

To understand the need when to call (or not to call) "inherited" from the Assign implementation, and how it relates to the AssignTo method, it's best to look at the TPersistent.Assign and TPersistent.AssignTo implementations:

procedure TPersistent.Assign(Source: TPersistent);
begin
  if Source <> nil then
    Source.AssignTo(Self)
  else
    raise EConvertError...
end;

procedure TPersistent.AssignTo(Dest: TPersistent);
begin
  raise EConvertError...
end;

(This is not the exact source code, I simplified it to hide the boring details abuot how the exception message is build. The exact source code, in the FPC standard library, can be found in the rtl/objpas/classes/persist.inc source file. It's behavior is 100% compatible with the Delphi standard library, as far as I know.)

So, if neither Assign nor AssignTo are overridden, then calling them will result in an exception. Also, note that there is no code in TPersistent implementation that automatically copies all the fields (or all the published fields) -- you need to do that yourself. You can use RTTI for that, but for simple cases you will probably just list the fields to be copied manually.

What are the cases when one cannot handle the assignment in his code?

When you have a class like TApple, your TApple.Assign implementation usually deals with copying fields that are specific to the TApple class (not to the TApple ancestor, like TFruit). So, the TApple.Assign implementation usually checks whether Source is TApple at the beginning, before copying apple-related fields. Then, it calls "inherited" to allow TFruit to handle rest of the fields.

Assuming that you implemented TFruit.Assign and TApple.Assign following the standard pattern, the effect is like this:

  • If you pass TApple instance to TApple.Assign, it will work and copy all the fields.
  • If you pass TOrange instance to TApple.Assign, it will work and only copy the common fields shared by both TOrange and TApple. IOW, the fields defined at TFruit.
  • If you pass TWerewolf instance to TApple.Assign, it will raise an exception (because TApple.Assign will call TFruit.Assign which will call TPersistent.Assign which raises an exception).

@michaliskambi
Copy link
Owner

@eugeneloza
Copy link
Contributor Author

Thanks a lot! Now it's all clear! :)

michaliskambi pushed a commit that referenced this issue Oct 6, 2018
Продолжаем дальше... :)
michaliskambi pushed a commit that referenced this issue Oct 6, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants