11.5.1: .NET generic types usage

Posted by Stefan Marquardt on 08-Sep-2016 04:50

Hi,

can I use generic types to create <List> for controls with OE 11.5.1?

define variable x as T60LoggClass.

x = new T60LoggClass().

x:Txt = "Test".

/* fails */ define variable a as class "System.Collections.Generic.List<T60LoggClass>".

 

It's a class with properties only like, the standard in C#

 

using Progress.Lang.*.

block-level on error undo, throw.

class T60LoggClass:

define public property IDUser as character no-undo

get.

set.

define public property Txt as character no-undo

get.

set.

end class.

 

Posted by Mike Fechner on 08-Sep-2016 05:00

Your class must inherit from System.Object.

Posted by Laura Stern on 09-Sep-2016 08:09

Re Mike's comment on 11.6 and the ProBindingSource...  Thanks for the vote of confidence, but alas, it is still a problem in 11.7. :-(

And an aside: this thread is about too many different topics!

All Replies

Posted by Mike Fechner on 08-Sep-2016 05:00

Your class must inherit from System.Object.

Posted by Stefan Marquardt on 08-Sep-2016 05:47

Thanks Mike for the hint.

class T60LoggClass inherits System.Object:

define public property IDUser as character no-undo

get.

set.

define public property Txt as character no-undo

get.

set.

end class.

----------------------------------------------------------------

/* class/syntax test: */

define variable x as T60LoggClass.

 x = new T60LoggClass().

x:Txt = "Test".

define variable a as "System.Collections.Generic.List<T60LoggClass>".  /* compile error */

Still this error:

Multiple markers at this line - ** ..Progress\Developer Studio 4.3.1\workspace\TestProject\FormLogging.cls Could not understand line 282. (196) - Invalid substitution type 'T60LoggClass'

supplied for a type parameter of Generic class System.Collections.Generic.List. (15214) - Invalid datatype specified: System.Collections.Generic.List. Specify a datatype such as 'character' or the name of a

class. (5638)

Posted by AdrianJones on 08-Sep-2016 05:56

here is a kb highlighting the issue: knowledgebase.progress.com/.../000040521

it offers apotential  workaround via using a wrapper

Posted by Mike Fechner on 08-Sep-2016 06:02

OK – overlooked that. The Generic List must be defined against a .NET type as a member. So System.Object in your case.
 
For ABL UserControls, you can define a list of Progress.Windows.UserControl. But you can’t define a list of ABL types.

Posted by Stefan Marquardt on 08-Sep-2016 06:39

System.Object was a good hint, now I got it running:

But, "new T60LoggClass()." is yery slow.

For each: FAST

assign List and display in Telerik grid control: FAST

But when I only add "new T60LoggClass()" to the each loop, I need to wait a few seconds.

Even when I remove every property from the class  creating ~70000 times a new "empty" class takes many seconds.

class T60LoggClass inherits System.Object final:

   define public property DatLog as char no-undo

   get.

   set.

   define public property IDUser as char no-undo

   get.

   set.

   define public property Handelse as char no-undo

   get.

   set.

define public property LastChanged as char no-undo

   get.

   set.

   define public property LastUser as char no-undo

   get.

   set.

define public property Logg as char no-undo

   get.

   set.

   define public property Txt as character no-undo

   get.

   set.

end class.

define variable rArrayList as class /* type parameter              */

 "System.Collections.Generic.List<System.Object>" no-undo.      

rArrayList = new "System.Collections.Generic.List<System.Object>"().      

define variable x as T60LoggClass.

for each T60Logg no-lock ...:            

           x = new T60LoggClass().

           assign

               x:IDUser      = T60Logg.IdUser

               x:Txt         = T60Logg.Txt

               x:DatLog      = string(T60Logg.DatLog)

               x:Handelse    = string(T60Logg.Handelse)

               x:LastChanged = string(T60Logg.LastChanged)

               x:LastUser    = T60Logg.LastUser

               x:Logg        = string(T60Logg.Logg).

            rArrayList:Add(x).

       end.

this-object:radGridView3:DataSource = rArrayList.

Posted by Stefan Marquardt on 08-Sep-2016 06:50

Unbelievable.

for each T60Logg no-lock where T60Logg.DatLog >= dt:

           create ttT60Logg.

           buffer-copy T60Logg to ttT60Logg.

end.

is 3 times faster than

for each T60Logg no-lock where T60Logg.DatLog >= dt:

           x = new T60LoggClass().

end

And this class is still empty.

Posted by marian.edu on 08-Sep-2016 07:32

Yep, anything that looks like an ORM is not really feasible in OO ABL at least not atm :(



Marian Edu

Acorn IT 
+40 740 036 212

Posted by Tim Kuehn on 08-Sep-2016 07:44

Each "NEW" is the rough equivalent of a RUN statement in terms of time, so you need to structure code like this accordingly. I'd suggest instantiating an OO structure outside of the loop and then make calls into the structure when in the loop.

PSC knows about this issue and is doubtless doing what they can about it.

Posted by Stefan Marquardt on 08-Sep-2016 08:16

But my list needs objects and a object is a class and I have no idea how to create an object without new.

BTW: Doing the same with much more objects needs <0,1 sec in C#

Posted by Stefan Marquardt on 08-Sep-2016 08:18

Or again a C# assembly, I pass a temp-table as parameter and it runs conversion into a List<objects>. (rough idea)

Posted by Mike Fechner on 08-Sep-2016 09:34

You can’t pass a temp-table to a .NET assembly.
 
Alternatives:
 

a)       WRITE-XML/JSON the temp-table table into a LONGCHAR and pass that to the assembly.

b)      Use a Progress.Data.BindingSource on a query over the TT-records. In .NET reflect the binding source to get the data

c)       Inside your Assembly, use the .NET Proxy (OpenClient) and call into an AppServer routine.

 
What’s your use-case?

Posted by Stefan Marquardt on 09-Sep-2016 01:51

Hi Mike,

thank you for the input.

This thread has to-do with https://community.progress.com/community_groups/openedge_development/f/19/t/26959

a) I need to check whether this can be an input for the Grid

b) This "fails" with sorting within the grid and I get no SortRequest in the BindingSource (other thread)

c) no option

I have an open case at PSC/Telerik why sorting with bounded data is so slow and awaiting an outcome.

The last was about optimizing ProBindingSource.

Posted by Mike Fechner on 09-Sep-2016 01:54

Both in a) and b) you'll have to write .NET Assemblies. In both cases you'd have to return a .NET Array of PONO's to the Grid.

But before wasting time, I'd test this on 11.6. Progress has fixed several issues between Telerik and the ProBindignSource in 11.6.

Posted by Stefan Marquardt on 09-Sep-2016 06:47

"Each "NEW" is the rough equivalent of a RUN statement in terms of time "

O.k., using -q reduced the creation from 50000 dummy instances of a class from 5 seconds to 1,5.

Still too slow.

Posted by Laura Stern on 09-Sep-2016 08:09

Re Mike's comment on 11.6 and the ProBindingSource...  Thanks for the vote of confidence, but alas, it is still a problem in 11.7. :-(

And an aside: this thread is about too many different topics!

Posted by WJCPvanBeek on 20-Sep-2016 13:08

Hi All,

because the classes in package System.Collections.Generic cannot contain instances of classes that inherit from Progress.Lang.Object, I have written my own code, that enables this. The basis, of course, is a temp-table, and the code is in an include.

an implementation example:

class Example.SomeCollection:

{Example/GenericCollection.i &PackageName = "Example"
                             &ClassName   = "SomeCollection"

                             &KeyPresent  = "YES"
                             &ValueType   = "Example.SomeClass"}
end class.

The include needs the packagename and the classname of the defined class. ValueType is the reference to your own class or any other classtype. Also any datatype will fit, even longchars, memptrs and raws. You can even specify an interface here. If you set KeyPresent (= character) to true, you will get a dictionarylike structure of Key-Value pairs.

If you open the preprocessor view you can view the methods/properties.

The class acts like a collection/dictionary, with methods like Add, Delete, IsEmpty, Contains, IndexOf. The class is not indexed, so you cannot use Item[n]. Instead you use the Item method, that receives a number or, if specified, a Key. It also has a built-in enumerator/iterator with methods ResetEnumerator, NextAvailable, Get{First|Next|Previous|Last}.

After a call to method Item or GetNext, you do not have to cast your object reference. It even contains the events ItemAdded and ItemDeleted to have other classes react to adding and deleting object references to the list. The event argument class is an instance of the collection class you are defining, but only with 1 instance. 

It also contain the properties:

  • Id = the number pertaining to the item (1 based, not 0 based)
  • Key = available when &KeyPresent = true, hold a character value 
  • Value = contains the reference to the current instance of the item.

After a call to Get{First|Next|Previous|Last} these properties are set.

If you are interested you can download a working example from https://prowill.livedrive.com/. Doubleclick the "GenericExample" icon and downlaod the "Examples" folder; copy it to your workspace and run SomeClassCollectionTest.p.

Feel free to make your own adjustments

Regards,

Will 

Posted by WJCPvanBeek on 20-Sep-2016 13:08

thumbs down should be Item [ n ]

Posted by Mike Fechner on 20-Sep-2016 13:28

Interesting. If you are interested in an alternative implementation, here’s another one:
 
 
Von: WJCPvanBeek [mailto:bounce-WJCPvanBeek@community.progress.com]
Gesendet: Dienstag, 20. September 2016 20:10
An: TU.OE.Development@community.progress.com
Betreff: RE: [Technical Users - OE Development] 11.5.1: .NET generic types usage
 
Update from Progress Community
 

Hi All,

because the classes in package System.Collections.Generic cannot contain instances of classes that inherit from Progress.Lang.Object, I have written my own code, that enables this. The basis, of course, is a temp-table, and the code is in an include.

an implementation example:

class Example.SomeCollection:

{Example/GenericCollection.i &PackageName = "Example"
                             &ClassName   = "SomeCollection"

                             &KeyPresent  = "YES"
                             &ValueType   = "Example.SomeClass"}
end class.

The include needs the packagename and the classname of the defined class. ValueType is the reference to your own class or any other classtype. Also any datatype will fit, even longchars, memptrs and raws. You can even specify an interface here. If you set KeyPresent (= character) to true, you will get a dictionarylike structure of Key-Value pairs.

If you open the preprocessor view you can view the methods/properties.

The class acts like a collection/dictionary, with methods like Add, Delete, IsEmpty, Contains, IndexOf. The class is not indexed, so you cannot use Item[n]. Instead you use the Item method, that receives a number or, if specified, a Key. It also has a built-in enumerator/iterator with methods ResetEnumerator, NextAvailable, Get{First|Next|Previous|Last}.

After a call to method Item or GetNext, you do not have to cast your object reference. It even contains the events ItemAdded and ItemDeleted to have other classes react to adding and deleting object references to the list. The event argument class is an instance of the collection class you are defining, but only with 1 instance. 

It also contain the properties:

  • Id = the number pertaining to the item (1 based, not 0 based)
  • Key = available when &KeyPresent = true, hold a character value 
  • Value = contains the reference to the current instance of the item.

After a call to Get{First|Next|Previous|Last} these properties are set.

If you are interested you can download a working example from https://prowill.livedrive.com/. Doubleclick the "GenericExample" icon and downlaod the "Examples" folder; copy it to your workspace and run SomeClassCollectionTest.p.

Feel free to make your own adjustments

Regards,

Will 

View online

 

You received this notification because you subscribed to the forum.  To unsubscribe from only this thread, go here.

Flag this post as spam/abuse.

 

Posted by WJCPvanBeek on 20-Sep-2016 14:14

Similar basis, how could it be an different :-), but because you have a ready-made class, you will still have to cast your references.

Posted by Mike Fechner on 20-Sep-2016 14:22

No, as far as I see your generic lists are based on include files with parameters, like ours:
 
 
The oList:GetItem(1) returns a string typed Customer reference, the Add only accepts a Customer etc.
 
The only difference is that you have the CLASS statement in the include as well. But besides that – same approach, just a slightly different implementation.

Posted by WJCPvanBeek on 20-Sep-2016 14:33

You're correct; didn't look any further then the List class.

Posted by Mike Fechner on 20-Sep-2016 14:40

I can also make the presentation I gave on a few conferences on that subject available. It’s still a shame the ABL does not have out of the box set classes.
Von: WJCPvanBeek [mailto:bounce-WJCPvanBeek@community.progress.com]
Gesendet: Dienstag, 20.
September 2016 21:36
An:
TU.OE.Development@community.progress.com
Betreff: RE: [Technical Users - OE Development] 11.5.1: .NET generic types usage
 
Update from Progress Community
 

You're correct; didn't look any further then the List class.

View online

 

You received this notification because you subscribed to the forum.  To unsubscribe from only this thread, go here.

Flag this post as spam/abuse.

 

Posted by WJCPvanBeek on 20-Sep-2016 15:02

I'd like to view that presentation. And I agree; it's a pity this doesn't come out-of-the-box.

Posted by Mike Fechner on 20-Sep-2016 15:18

See attached.

Posted by WJCPvanBeek on 20-Sep-2016 16:59

Thx Mike

This thread is closed