Page 1 of 2
Named parameters or How you can simplify your code
Posted: Tue Jan 24, 2017 6:48 am
by wriedmann
One of the interesting issues in Computer Programming is the possibility to solve problems in more than one way.
Please look at this code (creates a WPF grid with one row and one column):
Code: Select all
oGrid := Grid{}
oGrid:RowDefinitions:Add( RowDefinition{} )
oColDef := ColumnDefinition{}
oColDef:Width := GridLength{ 10 }
oGrid:ColumnDefinitions:Add( oColDef )
For every column with a defined width you need at least 3 lines of code!
Possibility 1 - a VO programmer would subclass the ColumnDefinition class:
Code: Select all
class MyColumnDefinition inherit ColumnDefinition
constructor()
super()
return
constructor( oWidth as GridLength )
super()
self:Width := oWidth
return
constructor( nWidth as int )
super()
self:Width := GridLength{ nWidth }
return
end class
and the relative use:
Code: Select all
oGrid := Grid{}
oGrid:RowDefinitions:Add( RowDefinition{} )
oGrid:ColumnDefinitions:Add( MyColumnDefinition{ 10 } )
oGrid:ColumnDefinitions:Add( MyColumnDefinition{ GridLength{ 10 } } )
Possibility 2 - named parameters, new with X#:
Code: Select all
oGrid := Grid{}
oGrid:RowDefinitions:Add( RowDefinition{} )
oGrid:ColumnDefinitions:Add( ColumnDefinition{}{ Width := GridLength{ 10 } } )
This code is the shortest but looks a bit ugly to me....
And
now possibility 3 - with extension methods in the Grid class:
Code: Select all
class GridExtensions
static method AddRow( self oGrid as Grid, nHeight as double ) as void
local oRowDefinition as RowDefinition
oRowDefinition := RowDefinition{}
oRowDefinition:Height := GridLength{ nHeight }
oGrid:RowDefinitions:Add( oRowDefinition )
return
static method AddRow( self oGrid as Grid, oHeight as GridLength ) as void
local oRowDefinition as RowDefinition
oRowDefinition := RowDefinition{}
oRowDefinition:Height := oHeight
oGrid:RowDefinitions:Add( oRowDefinition )
return
static method AddColumn( self oGrid as Grid, nWidth as double ) as void
local oColumnDefinition as ColumnDefinition
oColumnDefinition := ColumnDefinition{}
oColumnDefinition:Width := GridLength{ nWidth }
oGrid:ColumnDefinitions:Add( oColumnDefinition )
return
static method AddColumn( self oGrid as Grid, oWidth as GridLength ) as void
local oColumnDefinition as ColumnDefinition
oColumnDefinition := ColumnDefinition{}
oColumnDefinition:Width := oWidth
oGrid:ColumnDefinitions:Add( oColumnDefinition )
return
end class
and now how to use it:
Code: Select all
oGrid := Grid{}
oGrid:AddRow( 10 )
oGrid:AddRow( GridLength{ 1, GridUnitType.Star } )
oGrid:AddColumn( 10 )
oGrid:AddColumn( GridLength{ 1, GridUnitType.Pixel } )
You will find a XIDE export file with the full code attached to this message.
Named parameters or How you can simplify your code
Posted: Tue Jan 24, 2017 7:57 am
by Frank Maraite
Hi Wolfgang,
in general I like what you say here showing us named parameters and extension methods, but
...
General Guidelines
In general, we recommend that you implement extension methods sparingly and only when you have to. Whenever possible, client code that must extend an existing type should do so by creating a new type derived from the existing type. For more information, see Inheritance.
from
https://msdn.microsoft.com/en-us/library/bb383977.aspx
...
It is much more flexible creating your own derived Grid class and do the AddRow() method there as a real class member.
Extension methods are a perfect way to extend static classes from .NET namespaces. This way we can implement VO string functions and use them as System.String methods.
Keep up publishing your work.
Thanks
Frank
Named parameters or How you can simplify your code
Posted: Tue Jan 24, 2017 8:23 am
by wriedmann
Hi Frank,
I fully agree with what you say about extension methods - I added them only to show how different solutions can be.
My favorite, or better the code I use is based on this piece of code (if you look at my XIDE MVVM Enhanced Sample 2 you'll find very similar code):
Code: Select all
using System.Windows
using System.Windows.Controls
using System.Windows.Data
class MVVMGrid inherit Grid
constructor()
return
constructor( nColumns as int, nRows as int )
super()
self:Initialize( nColumns, nRows )
return
method Initialize( nColumns as int, nRows as int ) as logic
local nI as int
for nI := 1 upto nColumns
self:ColumnDefinitions:Add( ColumnDefinition{} )
next
for nI := 1 upto nRows
self:RowDefinitions:Add( RowDefinition{} )
next
return true
method SetRowHeightAll( oHeight as GridLength ) as void
foreach oRowDef as RowDefinition in self:RowDefinitions
oRowDef:Height := oHeight
next
return
method SetRowHeight( nRow as int, oHeight as GridLength ) as void
// 0-based
if self:RowDefinitions:Count > nRow
self:RowDefinitions[nRow]:Height := oHeight
else
throw ArgumentOutOfRangeException{ "nRow" }
endif
return
method SetRowHeight( nRow as int, nHeight as int ) as void
// 0-based
if self:RowDefinitions:Count > nRow
self:RowDefinitions[nRow]:Height := GridLength{ nHeight }
else
throw ArgumentOutOfRangeException{ "nRow" }
endif
return
method SetColWidth( nColumn as int, oWidth as GridLength ) as void
// 0-based
if self:ColumnDefinitions:Count > nColumn
self:ColumnDefinitions[nColumn]:Width := oWidth
else
throw ArgumentOutOfRangeException{ "nColumn" }
endif
return
method SetColWidth( nColumn as int, nWidth as int ) as void
// 0-based
if self:ColumnDefinitions:Count > nColumn
self:ColumnDefinitions[nColumn]:Width := GridLength{ nWidth }
else
throw ArgumentOutOfRangeException{ "nColumn" }
endif
return
method AddControl( oControl as UIElement, nColumn as int, nRow as int ) as void
Grid.SetColumn( oControl, nColumn )
Grid.SetRow( oControl, nRow )
self:Children:Add( oControl )
return
end class
This class not only simplifies column and row creation (making code more readable), but also reduces the code to add controls, making it cleaner (in my eyes).
Wolfgang
Named parameters or How you can simplify your code
Posted: Tue Jan 24, 2017 8:52 am
by Frank Maraite
Hi Wolfgang,
yes, that's fine. The only thing: I would not name it MVVMGrid. Your enhancements have nothing to do with MVVM. Show this in your name. WRGrid is not good too, but better. I name fmGrid, bad too :.( .
Another alternative:
Instead
...
method AddControl( oControl as UIElement, nColumn as int, nRow as int ) as void
Grid.SetColumn( oControl, nColumn )
Grid.SetRow( oControl, nRow )
self:Children:Add( oControl )
return
...
do
...
private method AddControl( Control as UIElement, columnNumber as int, rowNumber as int ) as void
Grid.SetColumn( Control, columnNumber )
Grid.SetRow( Control, rowNumber )
self:Children:Add( Control )
return
public method AddButton( columnNumber as int, rowNumber as int ) as WRButton
local Button as WRButton // or MVVMButto in your naming convention
Button := WRButton{}
AddControl( Button, columnNumber, rowNumber )
return Button
// In the calling method we can do additional things with the button control.
...
The same for all other kinds of controls. It seems overdone, but you will like it.
And for readability: There is no need for 'o' or 'n', but columnNumber says more than nColumn. With this verbose naming it's clear: it cannot be other than a number kind of type. And of course: all types are objects. These 'o's and 'n's are only visual dirt.
Frank
Named parameters or How you can simplify your code
Posted: Tue Jan 24, 2017 9:06 am
by wriedmann
Hi Frank,
I call these controls with the MVVM prefix, as they are specifically for MVVM (not the Grid, but the other ones, because of code like this:
Code: Select all
public new property Name as string
get
return super:Name
end get
set
super:Name := value
self:SetupBinding()
end set
end property
method SetupBinding() as void
local oBinding as Binding
oBinding := Binding{ self:Name }
oBinding:ValidatesOnDataErrors := true
oBinding:UpdateSourceTrigger := UpdateSourceTrigger.PropertyChanged
if ! String.IsNullOrEmpty( _cStringFormat )
oBinding:StringFormat := _cStringFormat
endif
self:SetBinding( TextBox.TextProperty, oBinding )
return
This code is from the MVVMTextBox class.
Yes, I could have named it differently....
And yes, I know you would use different method calls and much more private and protected methods, but I prefer the code how I have written it.
And about the prefixes: I prefer to write them as I feel they make the code better to read, and let me distinguish immediatly between the class name and the object iself (important since static methods are applying to the class and not the object):
is clearly differenced from
And yes, I prefer to have prefixes for the basic types like numerics, strings and logics.
These are all personal preferences, and fortunately everyone can have and maintain these (maybe the thing changes in the same company as I request this programming style in my small company)
Wolfgang
Named parameters or How you can simplify your code
Posted: Tue Jan 24, 2017 9:29 am
by Frank Maraite
Hi Wolfgang,
I often do
LOCAL myClass AS myClass
myClass := myClass{}
because in many situations the class type name is the best solution for its instance var name. With '.' and ':' different meaning in Vulcan there is no issue with static members and instance members.
A better exampel
CLASS ListOfmyClass inherit System.Collections.Generic.List<myClass>
END CLASS
local ListOfmyClass as ListOfmyClass
ListOfmyClass := ListOfmyClass{}
ListOfmyClass:Add( "123" )
? ListOfmyClass:Count:ToString()
// The code is out of my head now. Should work.
Why rename it? The ListOfmyClass is a ListOfmyClass. It worked in Vulcan and it works in X#.
As I understand X# takes ':' and '.' the same way and calls static or instant member upon there signature. This way I don't have to change my code if a member switches from static to instance for and back. Recompile is needed of course.
I cannot test this new X# behavior now, but will do ASAP.
Frank
Named parameters or How you can simplify your code
Posted: Tue Jan 24, 2017 9:44 am
by FFF
Frank,
pmfji, but i go with Wolfgang - for the simple reason, that "." and ":" are verrry prone to typos as they are to reading faults with tired eyes;)
Frankly, i can't quite get the reason for your crusade against hungarian notation - not everything gets better by using "long" names, as not everything gets bader when shortened to the max without loosing content...
Karl
Named parameters or How you can simplify your code
Posted: Tue Jan 24, 2017 9:58 am
by Frank Maraite
Karl,
because it's absolutly senseless. In times of intellisense you move your mouse pointer above your var and see the var type. Comments of a few of the best program theorists (copied from Wikipedia
https://en.wikipedia.org/wiki/Hungarian_notation):
---
Notable opinions
Robert Cecil Martin (against System Hungarian notation and all other forms of encoding):
... nowadays HN and other forms of type encoding are simply impediments. They make it harder to change the name or type of a variable, function, member or class. They make it harder to read the code. And they create the possibility that the encoding system will mislead the reader.[9]
Linus Torvalds (against Systems Hungarian):
Encoding the type of a function into the name (so-called Hungarian notation) is brain damaged—the compiler knows the types anyway and can check those, and it only confuses the programmer.[10]
Steve McConnell (for Apps Hungarian):
Although the Hungarian naming convention is no longer in widespread use, the basic idea of standardizing on terse, precise abbreviations continues to have value. Standardized prefixes allow you to check types accurately when you're using abstract data types that your compiler can't necessarily check.[11]
Bjarne Stroustrup (against Systems Hungarian for C++):
No I don't recommend 'Hungarian'. I regard 'Hungarian' (embedding an abbreviated version of a type in a variable name) as a technique that can be useful in untyped languages, but is completely unsuitable for a language that supports generic programming and object-oriented programming — both of which emphasize selection of operations based on the type and arguments (known to the language or to the run-time support). In this case, 'building the type of an object into names' simply complicates and minimizes abstraction.[12]
Joel Spolsky (for Apps Hungarian):
If you read Simonyi's paper closely, what he was getting at was the same kind of naming convention as I used in my example above where we decided that us meant unsafe string and s meant safe string. They're both of type string. The compiler won't help you if you assign one to the other and Intellisense [an Intelligent code completion system] won't tell you bupkis. But they are semantically different. They need to be interpreted differently and treated differently and some kind of conversion function will need to be called if you assign one to the other or you will have a runtime bug. If you're lucky. There's still a tremendous amount of value to Apps Hungarian, in that it increases collocation in code, which makes the code easier to read, write, debug and maintain, and, most importantly, it makes wrong code look wrong.... (Systems Hungarian) was a subtle but complete misunderstanding of Simonyi’s intention and practice.[13]
Microsoft's Design Guidelines[14] discourage developers from using Systems Hungarian notation when they choose names for the elements in .NET Class Libraries, although it was common on prior Microsoft development platforms like Visual Basic 6 and earlier. These Design Guidelines are silent on the naming conventions for local variables inside functions.
--------
This says it all
Frank
Named parameters or How you can simplify your code
Posted: Tue Jan 24, 2017 10:05 am
by Frank Maraite
Karl,
Karl Faller wrote:Frank,
pmfji, but i go with Wolfgang - for the simple reason, that "." and ":" are verrry prone to typos as they are to reading faults with tired eyes;)
Karl
as I wrote: there (should be) is no difference between '.' and ':'. As long as the method names are verbose and do what the name says it is ok, '.' or ':' gives the same result. For the user of that method it does not make any difference is it static or instance method. And of course: doing the right tests makes you sure your'e right.
Frank
Named parameters or How you can simplify your code
Posted: Tue Jan 24, 2017 10:06 am
by wriedmann
Hi Frank,
this is simply a question of personal preferences.
I can follow them was other people says, but I have not to do it.
It is like the use of the INI file: it is long time that Microsoft has discouraged the use, but I use them until today. Microsoft at least 15 years ago recommended to use the registry, and now recommends XML files - but until today I'm very happy with INI files.
Wolfgang