Considering that we now can have constants within records and classes -
Considering that we now can have constants within records and classes -
why can't we have resource strings in there?
type
TMyRec = record
const
ThisIsOK = 'String';
resourcestring <- [dcc32 Error] : E2029
ThisIsNotOK = 'string';
end;
[dcc32 Error] : E2029 'END' expected but 'RESOURCESTRING' found
why can't we have resource strings in there?
type
TMyRec = record
const
ThisIsOK = 'String';
resourcestring <- [dcc32 Error] : E2029
ThisIsNotOK = 'string';
end;
[dcc32 Error] : E2029 'END' expected but 'RESOURCESTRING' found
That might have to do with resources always being in the global scope of the EXE/DLL.
ReplyDeleteJeroen Wiert Pluimers But resourcestring naming can't be global?
ReplyDeleteunit A;
interface
resourcestring
ResName = 'name';
...
unit B;
interface
resourcestring
ResName = 'lastname';
How are they named globally?
AFAIK, you can also have resource strings in the implementation section of a unit?
The resource string is not a constant. It can vary in different translations.
ReplyDeleteInitialized variables in records and classes would be an interesting addition.
TMyRec = record
MyField: Integer = 111;
end;
T n T It is a constant from a compilation perspective, but it is correct that it's returned value can be changed with the translation tools at run time.
ReplyDeleteLars Fosdal The compiler can embed constants in the code. It's impossible for resource strings.
ReplyDeleteT n T Yes - the resource string is a run-time lookup.
ReplyDeletewhy not ? It could be just a kind of namespace after all.
ReplyDeleteAnd what size should the record become with a resourcestring inside, in your opinion?
ReplyDeleteWhy on earth would you want the same constant in every instance of a record?
ReplyDeletea constant is not duplicated, it is not part of the record (AFAIK) it is just a namespace question
ReplyDeleteJeff Dyer It would not be the same constant. The constants would be tied to a specific context, so that constants that happens to have similar or even identical names, could co-exist without namespace pollution
ReplyDeleteLars Fosdal good point?
ReplyDeleteahh, you want extra namespaces, ok
ReplyDeletehide this in a unit (x): resourcestring rs = 'rs';
ReplyDeleteuses x;
Ta = record
const
a: string = rs;
end;
Jeff Dyer constants declared inside types are associated with the type and not the instances of that type. So your question is a non-sequitur.
ReplyDeleteDavid Heffernan If all records of type Tx have the same constant in them , that adds no information to the system, as far as I can see. We can surely infer the type of the record using "if X is Tx", and the constant can't help us identify individual records, so I don't see what the point of this is at all.
ReplyDeleteThe point is to isolate resourcestrings for use in a limited context - i.e. to avoid the "global variable".
ReplyDeleteLars Fosdal That is mixing internal data structures with presentation. Not good IMO.
ReplyDeleteJeff Dyer what difference do you see between a const string and a resource string ?!
ReplyDeletePaul TOTH AFAIK the point of resource strings is to be able to build different versions of the application for different installations, for example multi language or different clients without resorting to configuration files. Those are the only times I've used them.
ReplyDeleteJeff Dyer Translation is the reason we do it as well. However, we have a large number of UI screens, and texts and terms can be similar but not necessarily identical in all contexts - hence using the same translation is at times completely wrong.
ReplyDeleteLike for any other type / const / variable scoping - it would be beneficial to be able to limit the scope of a resourcestring so that it is only accessible with the scope that it belongs to.
Lars Fosdal mixing presentation strings and business data leaves a bad smell to me.
ReplyDeleteJeff Dyer It's about using the type as a namespace, and keeping scope as narrow as possible.
ReplyDeleteDavid Heffernan It still smells.
ReplyDeleteWhy don't you just isolate like I showed yesterday, I don't think there is a better solution or ever will be. Records are not made to represent a namespace.
ReplyDeleteJeff Dyer In general, yes. However, when the presentation strings are fed as attributes to a generic class which purpose is to hold, sort, filter and format SQL query data specific to a presentation, and that is fed to an empty grid at runtime through a TGridView class which adapts the data to the specific grid type, we are in the presentation layer, not the business logic layer.
ReplyDeleteTOrderGridSet = class(TGridSet)
public
[GridAutoSize]
[InitField('Batch', 60),Autosize]
SchoolClass: TGridSet.TFieldString;
[InitField('Date, 90),Autosize]
ConsumptionDate: TGridSet.TFieldDate;
[InitField('Client', 60),Autosize]
ConsumerName: TGridSet.TFieldString;
...
Anyways - that "smell" thing is not the question here. The question is if there is any logical reason in the compiler for not allowing "private" resourcestring declarations.
ReplyDeleteJeff Dyer I'm not making any comment on how Lars Fosdal chooses to design his code. I'm explaining to you why you would declare constants inside compound types like records or classes.
ReplyDeleteLars Fosdal While technically possible, this is not a primary concern in Delphi. The unit is a namespace. If you need two different resource strings with the same name, you may need to split your unit into two ones.
ReplyDeleteT n T - If you split it into two units that means you introduce a unit sequence dependency since the last unit in scope is the one providing the value you refer to, and you don't get any hint or warning informing you that there is a namespace clash.
ReplyDeleteLars Fosdal you can prefix the name with the unit name to ensure that there's no collision.
ReplyDeletemy first impression with class method or class const and type was that it is not necessary because namespace are handled by units.
now I use them sometime to avoid splitting my code into several units :)
Paul TOTH I use class internal types and constants all the time.
ReplyDeleteNot only does it make scoping crystal clear - it also prevents OOP inheritance abuse, since the internal classes can't readily be hijacked for something they wasn't intended for.
I often do stuff like this
TMyDBClass = class(TDBObject)
type
fn = record const
view = 'v_MyDBTableView';
field1= 'field1';
field2 = 'field2';
end;
private
FField1: string;
FField2: string;
public
property Field1 read FField1 write FField1;
property Field2 read FField2 write FField2;
end;
so that I can stuff like
sql = QBuilder.Select([fn.field1, fn.Field2]).From(fn.view);
and do
Field1 := recset.FieldByName[fn.field1].AsString;
to make sure that all my field references are not simply multiple string constants, but one single constant. Makes it trivial to refactor field names and to avoid the accidentally misspelled field name in FieldByName['filed1'].
Lars Fosdal I do the same
ReplyDeleteTCustomer = record
CONST
TABLE_NAME = 'Customers';
KEY_FIELD = 'Id';
public
Name: string; // I miss the [size] suffix for Unicode string
!
Birth: TDateTime;