| Register | FAQ | Calendar | Search | Today's Posts | Mark Forums Read |
|
#1
| |||
| |||
| I titled the message with "Computer Science Logo Style" after Mr. Harvey's books, because in what other language could you create a object oriented framework with just a handful of procedures. I started a project to see if you could create an object oriented framework in Logo using Logo. What I created is what I call the Object Oriented Framework for Logo. I have tested it with both UCBLogo and FMSLogo, but I don't know if it will run on other versions of Logo (I doubt it unless those versions allow you to dynamically create procedures and macros.) The object oriented framework for Logo supports: classes, objects, properties and methods. It also supports constructors and destructors. For an example here is a rectangle class. It has four properties: x,y,height,width. And two methods: draw and erase. I am also going to create a constructor for it so I don't have to individualy set all the properties. defineclass "Rectangle defineproperty "Rectangle "x defineproperty "Rectangle "y defineproperty "Rectangle "Height defineproperty "Rectangle "Width definemethod "Rectangle "Draw [[] [ localmake "xcor xcor] [localmake "ycor ycor] [pu setxy thisrectangle.x thisrectangle.y pd] [repeat 2 [ fd thisrectangle.height rt 90 fd thisrectangle.width rt 90]] [pu setxy :x :y pd]] definemethod "Rectangle "Erase [[] [pe thisrectangle.draw ppt]] defineconstructor "Rectangle [[x y height width] [ thisrectangle.setx :x thisrectangle.sety :y thisrectangle.setheight :height thisrectangle.setwidth :width]] Now let's create four rectangles: r1, r2, r3, and r4. And then make them all spin. newrectangle "r1 100 100 150 50 newrectangle "r2 -100 -100 175 100 newrectangle "r3 100 -100 75 150 newrectangle "r4 -100 100 100 125 Now draw each of them: r1.draw r2.draw r3.draw r4.draw Or bet yet, make them all spin. cs forever [ foreach rectangleobjects [ tell ? [ thisrectangle.draw] ] wait 1 foreach rectangleobjects [ tell ? [thisrectangle.erase]] rt 1 ] I am creating a website that explains how to use it, but the website is taking me longer to create then the project :-) So I thought I would go ahead an post the project in the mean time. ;Object Oriented Framework for Logo to DefineClass :Name [:Constructor [] ] [ estructor []]if not name? "Classes [make "Classes []] if or (not word? :Name) (number? :Name) [(throw "error (se [DefineClass does not like] :Name [as input]))] if member? :Name :Classes [(throw "error (se [Class with name of] :Name [already exists]))] if greater? count gprop (word "Class_ :name) "Instances 0 [(throw "error [Can't Modify Class when objects of the class exist])] make "Classes lput :name :classes pprop (word "Class_ :name) "Properties [] pprop (word "Class_ :name) "Methods [] pprop (word "Class_ :name) "Instances [] bury pllist (word "Class_ :name) DefineConstructor :Name :Constructor DefineDestructor :Name estructorDefineReporter :Name DefinePredicate :Name end to DefineConstructor :Class :Constructor if not word? :Class [(throw "Error [Class Name must be a word])] if not list? :Constructor [(throw "Error (se [DefineConstructor doesn't like] :Constructor [as input]))] if not member? :Class :Classes [(throw "error (se [Class with name of] :Class [doesn't exist]))] if greater? count gprop (word "Class_ :Class) "Instances 0 [(throw "error [Can't Modify Class when objects of the class exist])] localmake "body [] localmake "params [] if Not Empty? :Constructor [make "params first :Constructor] make "params fput "Name aramsmake "body lput (se "New quoted :Class ":Name) :body if not Empty? :Constructor [make "body lput (se "localmake quoted "this ":name) :body make "body se :body bf :Constructor ] define (word "new :Class) fput arams :bodybury (word "new :Class) End to DefineDestructor :Class estructorif not word? :Class [(throw "Error [Class Name must be a word])] if not list? estructor [(throw "Error (se [DefineDestructor doesn'tlike] estructor [as input]))]if not member? :Class :Classes [(throw "error (se [Class with name of] :Class [doesn't exist]))] if greater? count gprop (word "Class_ :Class) "Instances 0 [(throw "error [Can't Modify Class when objects of the class exist])] localmake "body [] localmake "params [] if Not Empty? estructor [make "params first estructor]make "params fput "Name aramsif not Empty? estructor [make "body fput (se "localmake quoted"this ":name) :body make "body se :body bf estructor ]make "body lput (se "Delete quoted :Class ":Name) :body define (word "Delete :Class) fput arams :bodybury (word "Delete :Class) End to DefineMethod :Class :MethodName :MethodBody if not word? :Class [(throw "Error [Class Name must be a word])] if not word? :MethodName [(throw "Error [Method Name must be a word])] if not list? :MethodBody [(throw "Error (se [DefineMethod doesn't like] :MethodBody [as input]))] localmake "body [] local "params localmake "Methods gprop (word "Class_ :Class) "Methods if not member? :Class :Classes [(throw "error (se [Class with name of] :Class [doesn't exist]))] if greater? count gprop (word "Class_ :Class) "Instances 0 [(throw "error [Can't Modify Class when objects of the class exist])] pprop (word "Class_ :Class) "Methods DefineMethodHelper list :MethodName :MethodBody :Methods ;Create Class Method make "params fput "this first :MethodBody make "body fput arams bf :MethodBodydefine (word :Class "! :MethodName) :body bury (word :Class "! :MethodName) ;Create thisclass.methodname make "params first :MethodBody make "body [] foreach arams [make "body se :body (se "quoted word ": ?) ]make "body (se "output "\(list quoted (word :Class "! :MethodName) "quoted ":this :body "\) ) .defmacro (word "this :Class ". :MethodName) list aramsrunparse :body bury (word "this :Class ". :MethodName) end to DefineMethodHelper :Method :MethodList if empty? :MethodList [output fput :Method []] if equal? first :Method first first :Methodlist [output fput :Method bf :MethodList] output fput first :Methodlist DefineMethodHelper :Method bf :MethodList end to DefinePredicate :Class local "body make "body [output member? :Object gprop] make "body (se :body quoted (word "Class_ :Class) quoted "Instances) define (word :Class "Object?) list [Object] :body bury (word :Class "Object?) end to DefineProperty :Class :Property [:Getter []] [:Setter []] if not word? :Class [(throw "Error [Class Name must be a word])] if not word? :Property [(throw "error [Property Name must be a Word])] if not list? :Getter [(throw "error (se [DefineProperty doesn't like] :Getter [as input]))] if not list? :Setter [(throw "error (se [DefineProperty doesn't like] :Setter [as input]))] localmake "body [] localmake "Properties gprop (word "Class_ :Class) "Properties if not member? :class :Classes [(throw "error (se [Class with name of] :Class [doesn't exist]))] if greater? count gprop (word "Class_ :Class) "Instances 0 [(throw "error [Can't Modify Class when objects of the class exist])] pprop (word "Class_ :Class) "Properties DefinePropertyHelper (list :Property :Getter :Setter) :Properties ;Get Property ifelse empty? :Getter ~ [ ~ make "body (se "output "gprop "word quoted word :Class ". ":this quoted :Property) define (word :Class "! :Property) list [this] :body ] ~ [ ~ ifelse list? first :Getter ~ [ ~ define (word :Class "! :Property) fput [this] :Getter ] ~ [ ~ define (word :Class "! :Property) list [this] :Getter ] ~ ] bury (word :Class "! :Property) ;Get this Property make "body (list "Output "\(list quoted (word :Class "! :Property) "quoted ":this "\) ) .defmacro (word "this :Class ". :Property) list [] runparse :body bury (word "this :Class ". :Property) ;Set Property ifelse empty? :Setter ~ [ ~ make "body (se "pprop "word quoted word :Class ". ":this quoted :Property ":value) define (word :Class "!Set :Property) list [this value] :body ~ ] ~ [ ~ ifelse list? first :Setter ~ [ ~ define (word :Class "!Set :Property) fput [this value] :Setter ] ~ [ ~ define (word :Class "!Set :Property) list [this value] :Setter ] ~ ] bury (word :Class "!Set :Property) ;Set this Property make "body (list "Output "\(list quoted (word :Class "!Set :Property) "quoted ":this "quoted ":value "\) ) .defmacro (word "this :Class ".Set :Property) list [value] runparse :body bury (word "this :Class ".Set :Property) end to DefinePropertyHelper :Property :PropertyList if empty? :PropertyList [output fput :Property []] if equal? first :Property first first :PropertyList [output fput :Property bf :PropertyList] output fput first :PropertyList DefinePropertyHelper :Property bf :PropertyList end to DefineReporter :Class local "body make "body [output gprop] make "body (se :body quoted (word "Class_ :Class) quoted "Instances) define (word :Class "Objects) list [] :body bury (word :Class "Objects) end to Delete :Class :Object local "Properties local "Methods local "EraseList make "Properties gprop (word "Class_ :Class) "Properties make "Methods gprop (word "Class_ :Class) "Methods make "EraseList [] repeat count :Properties [ make "EraseList lput (word :Object ". first Item repcount :Properties) :EraseList make "EraseList lput (word :Object ".Set first Item repcount :Properties) :EraseList] repeat count :Methods [make "EraseList lput (word :Object ". first Item repcount :Methods) :EraseList] ;Erase Procedures make "Eraselist lput :EraseList [] ;Erase Variables - there won't be any but need to add to contents list make "Eraselist lput [] :EraseList ;Erase Property Lists - properties for object make "Eraselist lput (se (word :Class ". :Object) []) :EraseList Erase :EraseList pprop (word "Class_ :Class) "Instances Remove :Object gprop (word "Class_ :Class) "Instances end to DeleteClass :Class local "Properties local "Methods local "EraseList if (or (not word? :Class) (number? :Class) (empty? :Class)) [(throw "error (se [DeleteClass does not like] :Name [as input]))] if greater? count gprop (word "Class_ :Class) "Instances 0 [(throw "error [Can't Delete Class when objects of the class exist])] make "Properties gprop (word "Class_ :Class) "Properties make "Methods gprop (word "Class_ :Class) "Methods make "EraseList [] make "EraseList lput (word "new :Class) :EraseList make "EraseList lput (word "delete :Class) :EraseList make "EraseList lput (word :Class "Objects) :EraseList make "EraseList lput (word :Class "Object?) :EraseList repeat count :Properties [ make "EraseList lput (word :Class "! first Item repcount :Properties) :EraseList make "EraseList lput (word "this :Class ". first Item repcount :Properties) :EraseList make "EraseList lput (word :Class "!Set first Item repcount :Properties) :EraseList make "EraseList lput (word "this :Class ".Set first Item repcount :Properties) :EraseList] repeat count :Methods [make "EraseList lput (word :Class "! first Item repcount :Methods) :EraseList make "EraseList lput (word "this :Class ". first Item repcount :Methods) :EraseList] ;Erase Procedures make "Eraselist lput :EraseList [] ;Erase Variables - there won't be any but need to add to contents list make "Eraselist lput [] :EraseList ;Erase Property Lists - properties for object make "Eraselist lput (se (word "Class_ :Class ) []) :EraseList Erase :EraseList make "Classes Remove :Class :Classes end to GetProperty :Class :Object :Property output gprop (word :Class ". :Object) :Property end to New :Class :Object if not word? :Class [(throw "Error [Class Name must be a word])] if not word? :Object [(throw "error [Property Name must be a Word])] if StartsWith? "this :Object [(throw "error [Object name cannot start with "this])] local "Properties local "Methods local "Instances if not member? :class :Classes [(throw "error (se [Class with name of] :Class [doesn't exist]))] make "Properties gprop (word "Class_ :Class) "Properties make "Methods gprop (word "Class_ :Class) "Methods make "Instances gprop (word "Class_ :Class) "Instances repeat count :Properties [ NewPropHelper :Class first Item repcount :Properties :Object] repeat count :Methods [ NewMethodHelper :Class Item repcount :Methods :Object] pprop (word "Class_ :Class) "Instances lput :Object :Instances bury pllist (word :Class ". :Object) end to NewMethodHelper :ClassName :Method :ObjectName local "body ;Instance Set Property localmake "MethodName first :Method localmake "MethodParams first first bf :Method make "body [] foreach :Methodparams [make "body se :body (se "quoted word ": ?) ] make "body (se "output "\(list quoted (word :Class "! :MethodName) "quoted quoted :ObjectName :body "\) ) .defmacro (word :ObjectName ". :MethodName) list :MethodParams runparse :body bury (word :ObjectName ". :MethodName) end to NewPropHelper :ClassName :PropertyName :ObjectName local "body ;Instance Get Property make "body (se "Output "\(list quoted (word :ClassName "! :PropertyName) "quoted quoted :ObjectName "\) ) .defmacro (word :ObjectName ". :PropertyName) list [] runparse :body bury (word :ObjectName ". :PropertyName) ;Instance Set Property make "body (se "Output "\(list quoted (word :ClassName "! Set :PropertyName) "quoted quoted :ObjectName "quoted ":value "\) ) .defmacro (word :ObjectName ".Set :PropertyName) list [value] runparse :body bury (word :ObjectName ".Set :PropertyName) End to SetProperty :Class :Object :Property :Value pprop (word :Class ". :Object) :Property :Value end to StartsWith? :test :input if less? count :input count :test [output "false] foreach :test [ if not equal? item # :input ? [output "false]] output "true end to TalkTo bjectifelse word? :Object [make "this bject] [ifelse empty? bject[ERN "This] [(throw "error (se [Talkto does not like] bject [asinput]))]] end to Tell :this :InstructionList run :InstructionList end |
|
#2
| |||
| |||
| danielwb@insightbb.com writes: >I started a project to see if you could create an object oriented >framework in Logo using Logo. What I created is what I call the Object >Oriented Framework for Logo. Very elegant! Have you thought about inheritance? It shouldn't be that hard to implement, but there are design choices to be made, especially if you allow multiple inheritance. I've had some students working on reimplementing the Object Logo version of OOP (which is prototype-style rather than class/instance-style) for UCBLogo. I'll have to work out whether we can just put it in the Logo library instead. The performance might not be good enough for complicated programs, and Logo itself has to be able to use it (to support things like multiple turtles, multiple windows, etc.). |
|
#3
| |||
| |||
| On May 23, 9:05*pm, b...@cs.berkeley.edu (Brian Harvey) wrote: > danie...@insightbb.com writes: > >I started a project to see if you could create an object oriented > >framework in Logo using Logo. What I created is what I call the Object > >Oriented Framework for Logo. > > Very elegant! > > Have you thought about inheritance? *It shouldn't be that hard to implement, > but there are design choices to be made, especially if you allow multiple > inheritance. > > I've had some students working on reimplementing the Object Logo version of > OOP (which is prototype-style rather than class/instance-style) for UCBLogo. > I'll have to work out whether we can just put it in the Logo library instead. > The performance might not be good enough for complicated programs, and Logo > itself has to be able to use it (to support things like multiple turtles, > multiple windows, etc.). Yes, Inheritance is the next thing I want to implement. I am thinking about giving the DefineClass optional parameters which will be a list of classes to Inherit from. From a user standpoint the most important thing I need to implement, is a way be able to enter the class from the Editor, so that I can use more friendler syntax like: Class Rectangle Property X Property Y Method New :x :y :height :width thisrectangle.setx :x thisrectangle.sety :y thisrectangle.setheight :height thisrectangle.setwidth :width End Method .... End Class I would like to try the Object Logo for UCBLogo :-) Take care, Dan |
|
#4
| |||
| |||
| danielwb@insightbb.com writes: >Yes, Inheritance is the next thing I want to implement. I am thinking >about giving the DefineClass optional parameters which will be a list >of classes to Inherit from. Who wins, a first grandparent's method or a second parent's own method? Does it matter if the grandparent is common to the two parent classes? >From a user standpoint the most important thing I need to implement, >is a way be able to enter the class from the Editor, so that I can use >more friendler syntax like: > >Class Rectangle >Property X >Property Y >Method New :x :y :height :width >thisrectangle.setx :x >thisrectangle.sety :y >thisrectangle.setheight :height >thisrectangle.setwidth :width >End Method >.... >End Class If you'll settle for Class "Rectangle and the rest the way you have it, just put a read loop in the Class procedure that keeps calling READLIST, making any syntactic changes you want and then sending the read lines off to Logo. (For METHOD you'd accumulate all the body lines up to END METHOD in a list.) >I would like to try the Object Logo for UCBLogo :-) The project, like the wxWidgets project, is currently somewhere in the tail of the 90/10 rule. |
|
#5
| |||
| |||
| On May 23, 10:13*pm, b...@cs.berkeley.edu (Brian Harvey) wrote: > danie...@insightbb.com writes: > >Yes, Inheritance is the next thing I want to implement. *I am thinking > >about giving the DefineClass optional parameters which will be a list > >of classes to Inherit from. > > Who wins, a first grandparent's method or a second parent's own method? > Does it matter if the grandparent is common to the two parent classes? > > >From a user standpoint the most important thing I need to implement, > >is a way be able to enter the class from the Editor, so that I can use > >more friendler syntax like: > > >Class Rectangle > >Property X > >Property Y > >Method New :x :y :height :width > >thisrectangle.setx :x > >thisrectangle.sety :y > >thisrectangle.setheight :height > >thisrectangle.setwidth :width > >End Method > >.... > >End Class > > If you'll settle for > * * * * Class "Rectangle > and the rest the way you have it, just put a read loop in the Class procedure > that keeps calling READLIST, making any syntactic changes you want and then > sending the read lines off to Logo. *(For METHOD you'd accumulate all the body > lines up to END METHOD in a list.) > That would make it better than it is now. But what I really want is to be able to is open it up in the Editor. Currently there isn't any easy way to delete or edit properties or methods. To edit methods you basically have to reenter the command again that will replace it. You can't delete properties or methods at all. It is an easy fix though, I could easily implement the procedure to delete a property or method. You could create a procedure called initialize. Which defined all your classes. Which would let you then edit that procedure which meant that you are not limited to the commandline. But again I think you could write a logo procedure to read what was written in the editor and parse it to call all the procedures to define the class. (If I am not mistaken you can call the Editor to open any file, or can you only do it FMSLogo?) > >I would like to try the Object Logo for UCBLogo :-) > > The project, like the wxWidgets project, is currently somewhere in the tail > of the 90/10 rule. |
|
#6
| |||
| |||
| danielwb@insightbb.com writes: >That would make it better than it is now. But what I really want is >to be able to is open it up in the Editor. Currently there isn't any >easy way to delete or edit properties or methods. To edit methods you >basically have to reenter the command again that will replace it. You >can't delete properties or methods at all. It is an easy fix though, >I could easily implement the procedure to delete a property or >method. The EDITFILE command takes a filename as input, edits that file, and then loads it into Logo. I thought you could put a DefineClass in the file, followed by the lines that DefineClass would read, until I tried the experiment just now. Turns out that UCBLogo remembers the input stream it's currently loading from separately from the current READER stream! Is this a bug? I think the motivation is that if you've done a SETREAD, and then you do a LOAD, that shouldn't change the READER stream for reading you do after the load. But LOAD could push and pop READER, and set it to be the same as the loadstream temporarily. Then reads (READLIST or READWORD, etc.) within the loaded file would read from the file (unless you did an explicit SETREAD within the loaded file). Does anything depend on the current behavior? That is, does anyone load files that have to read from the keyboard during the loading? |
|
#7
| |||
| |||
| On May 24, 3:36*am, b...@cs.berkeley.edu (Brian Harvey) wrote: > danie...@insightbb.com writes: > >That would make it better than it is now. *But what I really want is > >to be able to is open it up in the Editor. *Currently there isn't any > >easy way to delete or edit properties or methods. *To edit methods you > >basically have to reenter the command again that will replace it. You > >can't delete properties or methods at all. *It is an easy fix though, > >I could easily implement the procedure to delete a property or > >method. > > The EDITFILE command takes a filename as input, edits that file, and then > loads it into Logo. *I thought you could put a DefineClass in the file, > followed by the lines that DefineClass would read, until I tried the > experiment just now. *Turns out that UCBLogo remembers the input stream it's > currently loading from separately from the current READER stream! > > Is this a bug? *I think the motivation is that if you've done a SETREAD,and > then you do a LOAD, that shouldn't change the READER stream for reading you > do after the load. *But LOAD could push and pop READER, and set it to be > the same as the loadstream temporarily. *Then reads (READLIST or READWORD, > etc.) within the loaded file would read from the file (unless you did an > explicit SETREAD within the loaded file). > > Does anything depend on the current behavior? *That is, does anyone load > files that have to read from the keyboard during the loading? On the subject of Inheritance how do you think it should work. For instance, suppose you have a scenario where you create a base class, then create a new class that inherits from the base class. Then you modify the base class by either adding or changing properties or methods. Should the inherited class reflect the old base class or the new modified base class? Or suppose scenario two. You create a base class. You create a new class that inherits from the base class. Then you create a couple of objects from the new class: say object1 and object2. Then you modify the base class, and then create two more objects of the new class: object3 and object4. Should object1 and object2 reflect the old base class and object3 and object4 reflect the new class. Here are my thoughts on the two scenarios above. In scenario one, the inherited class will reflect the new base class. From an implementation standpoint, the properties and method won't be copied to the inherited class. There will be a new property list for a class called Inheritance. And when an object is created it will pull not only properties and methods for the class, but properties and method for the base classes as well. In scenario number two, I won't let you modify a base class if there are instances of a class that exist that have been inherited. In the current design, I think it allowing this leads to an inconsistent state. Thanks, Dan |
|
#8
| |||
| |||
| On May 29, 10:42*pm, danie...@insightbb.com wrote: > On May 24, 3:36*am, b...@cs.berkeley.edu (Brian Harvey) wrote: > > > > > > > danie...@insightbb.com writes: > > >That would make it better than it is now. *But what I really want is > > >to be able to is open it up in the Editor. *Currently there isn't any > > >easy way to delete or edit properties or methods. *To edit methods you > > >basically have to reenter the command again that will replace it. You > > >can't delete properties or methods at all. *It is an easy fix though, > > >I could easily implement the procedure to delete a property or > > >method. > > > The EDITFILE command takes a filename as input, edits that file, and then > > loads it into Logo. *I thought you could put a DefineClass in the file, > > followed by the lines that DefineClass would read, until I tried the > > experiment just now. *Turns out that UCBLogo remembers the input stream it's > > currently loading from separately from the current READER stream! > > > Is this a bug? *I think the motivation is that if you've done a SETREAD, and > > then you do a LOAD, that shouldn't change the READER stream for reading you > > do after the load. *But LOAD could push and pop READER, and set it to be > > the same as the loadstream temporarily. *Then reads (READLIST or READWORD, > > etc.) within the loaded file would read from the file (unless you did an > > explicit SETREAD within the loaded file). > > > Does anything depend on the current behavior? *That is, does anyone load > > files that have to read from the keyboard during the loading? > > On the subject of Inheritance how do you think it should work. *For > instance, suppose you have a scenario where you create a base class, > then create a new class that inherits from the base class. Then you > modify the base class by either adding or changing properties or > methods. *Should the inherited class reflect the old base class or the > new modified base class? > > Or suppose scenario two. *You create a base class. *You create a new > class that inherits from the base class. *Then you create a couple of > objects from the new class: say object1 and object2. *Then you modify > the base class, and then create two more objects of the new class: > object3 and object4. > Should object1 and object2 reflect the old base class and object3 and > object4 reflect the new class. > > Here are my thoughts on the two scenarios above. In scenario one, the > inherited class will reflect the new base class. *From an > implementation standpoint, the properties and method won't be copied > to the inherited class. *There will be a new property list for a class > called Inheritance. *And when an object is created it will pull not > only properties and methods for the class, but properties and method > for the base classes as well. > > In scenario number two, I won't let you modify a base class if there > are instances of a class that exist that have been inherited. *In the > current design, I think it allowing this leads to an inconsistent > state. > > Thanks, > > Dan- Hide quoted text - > > - Show quoted text - Let me clarify the above statement: Not an inconsistent state, but an unpredictable state (i.e.:it can cause unpredictable errors: for instance modifying the number a parameters that a method takes). |
|
#9
| |||
| |||
| danielwb@insightbb.com writes: >unpredictable state (i.e.:it can cause unpredictable errors: for >instance modifying the number a parameters that a method takes). Yes, this is a concern, but it is even without objects or inheritance if you try to do any kind of optimization. What UCBLogo does is keep a generation count that gets incremented (actually what it really is is a newly allocated (cons nil nil) pair rather than a number) every time a procedure changes arity. Every line of runparsed code also remembers its generation; if they don't match, the line is reparsed. This allows procedure redefinition freely. (To be sure, changing the arity without changing the caller will probably be a user bug! But it won't crash Logo. :-) I firmly believe that everything should be redefinable dynamically; this is one of the great strengths of interpreted, interactive languages. Admittedly it can be confusing if two objects have same-name different-arity methods, but you have to cope with it; the cost is reparsing callers to those methods on every call. |
![]() |
| Thread Tools | |
| Display Modes | |
In an effort to better serve ads to our visitors, cookies are used on objectmix.com. For more information, check out our Privacy Policy.