Named parameters or How you can simplify your code

Public forum to share code snippets, screen shorts, experiences, etc.
User avatar
wriedmann
Posts: 3755
Joined: Mon Nov 02, 2015 5:07 pm
Location: Italy

Named parameters or How you can simplify your code

Post 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.
Attachments
GridConstruction.zip
(2.22 KiB) Downloaded 53 times
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
Frank Maraite
Posts: 178
Joined: Sat Dec 05, 2015 10:44 am
Location: Germany

Named parameters or How you can simplify your code

Post 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
User avatar
wriedmann
Posts: 3755
Joined: Mon Nov 02, 2015 5:07 pm
Location: Italy

Named parameters or How you can simplify your code

Post 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
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
Frank Maraite
Posts: 178
Joined: Sat Dec 05, 2015 10:44 am
Location: Germany

Named parameters or How you can simplify your code

Post 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
User avatar
wriedmann
Posts: 3755
Joined: Mon Nov 02, 2015 5:07 pm
Location: Italy

Named parameters or How you can simplify your code

Post 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):

Code: Select all

MyObject.MyStaticMethod()
is clearly differenced from

Code: Select all

oMyObject:MyMethod()
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
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
Frank Maraite
Posts: 178
Joined: Sat Dec 05, 2015 10:44 am
Location: Germany

Named parameters or How you can simplify your code

Post 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
FFF
Posts: 1580
Joined: Fri Sep 25, 2015 4:52 pm
Location: Germany

Named parameters or How you can simplify your code

Post 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
Regards
Karl
(on Win8.1/64, Xide32 2.20, X#2.20.0.3)
Frank Maraite
Posts: 178
Joined: Sat Dec 05, 2015 10:44 am
Location: Germany

Named parameters or How you can simplify your code

Post 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
Frank Maraite
Posts: 178
Joined: Sat Dec 05, 2015 10:44 am
Location: Germany

Named parameters or How you can simplify your code

Post 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
User avatar
wriedmann
Posts: 3755
Joined: Mon Nov 02, 2015 5:07 pm
Location: Italy

Named parameters or How you can simplify your code

Post 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
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
Post Reply