Original in russian: http://programmingmindstream.blogspot.ru/2014/08/blog-post_85.html
I wіll definitely not break new ground, but I will write.
It can be done in this way:
Or in this way:
Both these options seem identical.
But, as for me, the second one is more “tasty” as well as more readable.
In debugging – more useful.
Why useful? Because you can set one break-point to EmyException.Check compared to the bags of them throughout the code.
Perhaps, it is included in standard library on new versions of Delphi. I actually did not found out. But we have been using this approach already for a long time and we like it.
Of course, it can be enhanced and improved.
For example, you could not pass the string but “generate” it inside Check. Generally, there is lots of alternatives.
I will also note that I’ve seen enough people who’ve forgotten raise.
It means they wrote like this:
But not like this:
And about the "expansions”.
For example, you can do like this:
Of course, it is only a “prototype”, not a true working code.
What’s so good of the “prototype”? It is that SomeComplexExpression will be evaluated only once.
Of course, you could do with a “locale variable” – in the simplest cases.
Besides, we can write like this:
In response to the commentary: http://programmingmindstream.blogspot.ru/2014/08/blog-post_85.html?showComment=1408566592655#c1216957169742679866
As for “using just a procedure”, we’ve used them too, of course:
One more thing.
What will happen if we write like this:
Exception of which class will trigger: EmyException or EmyException2?
As could be expected, it is EmyException2 :-)
It concerns “why class method and not just function”.
That is in some way an answer to: http://programmingmindstream.blogspot.ru/2014/08/blog-post_85.html?showComment=1408649403323#c8271514992700352647)
By the way, here is an example of what was written at the beginning:
IsValidPosition and IsValidLink are predicates.
In other words, function (aData: Int64): Boolean of object;
Check in this case looks like this:
What else do we need it for?
So that not to make a mess in the function Format and not to debug the raised exception, that will appear but not “tell the truth”.
It is obvious that the example of the code does not look good, but I did not refined it for purpose.
Of course, “tastes differ”, but I like it more than with a “band” of raise.
Try to write with raise and show that it will “be shorter”. I’ll be glad to learn.
By the way, directing of stack to the log file helps to identify the string from which an exception sprang. May be, I will write about it some time.
(Meanwhile here a discussion takes place).
And I will note: we could write something like:
But I do not do it consciously, since writing is surely shorter and more readable.
But! Using this approach it is more difficult to find out the real source of an error.
I could also write with, I know it. But I personally am an “ideological enemy” of with, especially in Delphi version without “reverse stability”.
Do you understand what "reverse stability" is? Or do I “think up my own terms” again?
Just in case I’ve written this one: Briefly. About “reverse stability”
I wіll definitely not break new ground, but I will write.
It can be done in this way:
type EmyException = class(Exception) end;//EmyException ... if not Condition1 then raise EMyException.Create('Some string1'); ... if not Condition2 then raise EMyException.Create('Some string2');
Or in this way:
type EmyException = class(Exception) public class procedure Check(aCondition: Boolean; const aMessage: String); end;//EmyException ... class procedure EmyException.Check(aCondition: Boolean; const aMessage: String); begin if not aCondition then raise Self.Create(aMessage); end; ... EMyException.Check(Condition1, 'Some string1'); ... EMyException.Check(Condition2, 'Some string2');
Both these options seem identical.
But, as for me, the second one is more “tasty” as well as more readable.
In debugging – more useful.
Why useful? Because you can set one break-point to EmyException.Check compared to the bags of them throughout the code.
Perhaps, it is included in standard library on new versions of Delphi. I actually did not found out. But we have been using this approach already for a long time and we like it.
Of course, it can be enhanced and improved.
For example, you could not pass the string but “generate” it inside Check. Generally, there is lots of alternatives.
I will also note that I’ve seen enough people who’ve forgotten raise.
It means they wrote like this:
Exception.Create('aMessage');
But not like this:
raise Exception.Create('aMessage');
And about the "expansions”.
For example, you can do like this:
type TMyPredicate = reference to function (aData: Integer): Boolean; EmyException = class(Exception) public class procedure Check(aCondition: Boolean; const aMessage: String); overload; class procedure Check(aCondition: TMyPredicate; const aMessage: String; aData: Integer); overload; end;//EmyException ... class procedure EmyException.Check(aCondition: Boolean; const aMessage: String); begin if not aCondition then raise Self.Create(aMessage); end; class procedure EmyException.Check(aCondition: TMyPredicate; const aMessage: String; aData: Integer); begin if not aCondition(aData) then raise Self.Create('InvalidData: ' + IntToStr(aData) + aMessage); end; ... EMyException.Check(Condition1, 'Some string1'); ... EMyException.Check( function (aData: Integer): Boolean; begin Result := IsValid(aData); end;, 'Some string2', SomeComplexExpression);
Of course, it is only a “prototype”, not a true working code.
What’s so good of the “prototype”? It is that SomeComplexExpression will be evaluated only once.
Of course, you could do with a “locale variable” – in the simplest cases.
Besides, we can write like this:
var SomeLocalData : Integer; EMyException.Check( function (aData: Integer): Boolean; begin Result := (aData = SomeLocalData); end;, 'Some string2', SomeComplexExpression);
In response to the commentary: http://programmingmindstream.blogspot.ru/2014/08/blog-post_85.html?showComment=1408566592655#c1216957169742679866
As for “using just a procedure”, we’ve used them too, of course:
function Ht(ID : LongInt) : LongInt; {var nDosError : SmallInt; // Here the code returned by DOS will be entered nOperation: SmallInt; // Here the code of the operation causing an error will be entered lErrstr : array[0..1000] of AnsiChar; lErrstr2 : PAnsiChar; } begin Result := ID; if lNeedStackOut_ErrNum <> 0 then begin l3System.Stack2Log(Format('HTERROR = %d STACK OUT', [lNeedStackOut_ErrNum])); lNeedStackOut_ErrNum := 0; end; { if ID = -1 then lErrstr2 := htExtError(nDosError, nOperation, @lErrstr[0]); } if ID < 0 then raise EHtErrors.CreateInt(ID); end; .... Ht(htOpenResults(Masks,ROPEN_READ,@FldArr,FldCount)); .... Ht(htDeleteRecords(TmpList)); .... Ht(htOpenResults(ValList,ROPEN_READ,nil,0));
One more thing.
What will happen if we write like this:
type EmyException2 = class(EmyException) end;//EmyException2 ... EmyException2.Check(aCondition, aMessage);
Exception of which class will trigger: EmyException or EmyException2?
As could be expected, it is EmyException2 :-)
It concerns “why class method and not just function”.
That is in some way an answer to: http://programmingmindstream.blogspot.ru/2014/08/blog-post_85.html?showComment=1408649403323#c8271514992700352647)
By the way, here is an example of what was written at the beginning:
Em3InvalidStreamPos.Check(Self.IsValidPosition, aHeader.f_Name, l_Pos); Em3InvalidStreamSize.Check(Self.IsValidPosition, aHeader.f_Name, aHeader.f_TOCItemData.rBody.rRealSize); Em3InvalidStreamPos.Check(Self.IsValidLink, aHeader.f_Name, aHeader.f_TOCItemData.rBody.RTOCBuffRootPosition); Em3InvalidStreamPos.Check(Self.IsValidLink, aHeader.f_Name, aHeader.f_TOCItemData.rBody.RTOCItemListPosition); Em3InvalidStreamPos.Check(Self.IsValidLink, aHeader.f_Name, aHeader.f_TOCItemData.RNextPosition);
IsValidPosition and IsValidLink are predicates.
In other words, function (aData: Int64): Boolean of object;
Check in this case looks like this:
type TInt64Predicate = function (aData: Int64): Boolean of object; ... class procedure Em3InvalidStreamData.Check(aCondition: TInt64Predicate; aName : String; aData : Int64); begin if not aCondition(aData) then raise Self.CreateFmt('Invalid data %d in file %s', [aName, aData]); end; ... Em3InvalidStreamPos = class(Em3InvalidStreamData); Em3InvalidStreamSize = class(Em3InvalidStreamData);
What else do we need it for?
So that not to make a mess in the function Format and not to debug the raised exception, that will appear but not “tell the truth”.
It is obvious that the example of the code does not look good, but I did not refined it for purpose.
Of course, “tastes differ”, but I like it more than with a “band” of raise.
Try to write with raise and show that it will “be shorter”. I’ll be glad to learn.
By the way, directing of stack to the log file helps to identify the string from which an exception sprang. May be, I will write about it some time.
(Meanwhile here a discussion takes place).
And I will note: we could write something like:
Em3InvalidStreamPos.Check(Self.IsValidPosition, aHeader.f_Name, [l_Pos, aHeader.f_TOCItemData.rBody.rRealSize, aHeader.f_TOCItemData.rBody.RTOCBuffRootPosition, aHeader.f_TOCItemData.rBody.RTOCItemListPosition, aHeader.f_TOCItemData.RNextPosition]);
But I do not do it consciously, since writing is surely shorter and more readable.
But! Using this approach it is more difficult to find out the real source of an error.
I could also write with, I know it. But I personally am an “ideological enemy” of with, especially in Delphi version without “reverse stability”.
Do you understand what "reverse stability" is? Or do I “think up my own terms” again?
Just in case I’ve written this one: Briefly. About “reverse stability”
Комментариев нет:
Отправить комментарий