Always, ALWAYS initialize!

What output would you expect from such a piece of code (VB.net, of course):

    Imports System.Globalization
    .... 
    Dim CurrentCalendar As System.Globalization.Calendar = _
                CultureInfo.CurrentUICulture.Calendar

    'We want to output all the dates in the current year for every month.
    For iMonth As Integer = 1 To 12
        Dim sOutput As String
        'accumulate all the dates
        For iDay As Integer = 1 To CurrentCalendar.GetDaysInMonth(Now.Year, iMonth)
            sOutput &= iDay & ","
        Next
         'print out all the accumulated dates
        Console.WriteLine(sOutput.TrimEnd(","c))
    Next

I’d say it must be

1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31

but, the hell, no, it is:

(1st line)
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
(2nd line)
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,1,2,3,
                   ...other Februray dates..28
(3rd line)
...January dates...February dates...March dates...
(4th line)
...January dates...February dates...March dates...April dates...
(5th line)
...January dates...February dates...March dates...May dates...   
....
(12th line)
...January dates...February dates...March dates...May dates...December dates...

This means that even though the variable s is re-declared, it is not being re-initialized! Just… keep it in mind.

Hint: instead of being Dim sOutput As String it should be Dim sOutput As String="" if we still want to keep this syntax.

IsUserInGroup updated

In an earlier article I’ve been talkin about how to check if a user belongs to a specific domain group. However, the code in that article doesn’t work on local groups and WinNT ADSI provider. I’ve made an update to the code posted there, this should work both on domain and local groups.

Usage:

If IsUserInGroup("Contoso\JohnDoe", "Contoso\Managers") Then
   '...do something
End If

Notes:

  • Option Strict Off. Sorry, I’m using the GetObject magic function here, so there’s no way I could leave the option strict on.
  • Seems to be quite slow when local groups are scanned from outside.
  • Could be a problem when you have users from two different domains + local users.

    Continue reading IsUserInGroup updated

Does this user belong to that domain group?

UPD: This works only on LDAP groups and LDAP:// provider, not WinNT groups!

If we know two things – a login name of a user for instance northwind\JohnDoe and a domain group northwind\board. We want to check if John Doe belongs to the “board” group or not.

This is how you find it: IsUserMember( "northwind\JohnDoe", "northwind\board")

Continue reading Does this user belong to that domain group?

Month names. i18n, you know

Whenever you see something like this you should come to an idea something is wrong (for instance, the guy who wrote this is still alive):

'month names in Latvian
Dim sMonth() As String = _
            {"Janvāris", "Februāris", "Marts", "Aprīlis", _
            "Maijs", "Jūnijs", "Jūlijs", "Augusts", _
            "Septembris", "Oktobris", "Novembris", "Decembris"}

Dim oListItem as ListItem
For iMonth as Integer = 1 To 12
  oListItem = New ListItem(sMonth(iMonth-1),  iMonth.ToString())
  Me.lstYears.Items.Add(oListItem)
Next

Explanation – if MS Windows knows quite well how months are named in Latvia, why should you try to be smarter than the big brother? Another explanation – if this is a web project (and in my case it is), you should think of internationalization whenever possible.

So a more correct way to add month names to a dropdown menu is:

Imports System.Globalization
....
Dim oListItem as ListItem
For iMonth As Integer = 0 To 11
   oEntry = New Web.UI.WebControls.ListItem( _
         CultureInfo.CurrentUICulture.DateTimeFormat.MonthNames(iMonth), _
            iMonth.ToString)
   Me.lstMonths.Items.Add(oEntry)
Next

And, to be even more precise, you could first check how many months there are in the current culture:

For iMonth As Integer = 0 To _
                CultureInfo. CurrentUICulture.Calendar. _ 
                GetMonthsInYear(Now.Year) - 1
    ....

/Just trying to be a good boy and use the facilities provided by the framework/

Parse the Enum!

What if we need to save the value of an instance of an enumeration (Enum) as string and then get back the value again?

For instance, we have the following code:

Dim eDay As System.DayOfWeek = DayOfWeek.Monday

So now we can get a textual representation of eDay using the built-in ToString() method:

MessageBox.Show(eDay.ToString())

This yields a messagebox saying “Monday”. But how do we do the reverse (i.e., we know only the string representation “Monday”, but we need the enum value)? The trivial approach would be:

    Dim sStr As String = "Monday"
    Dim eDay As System.DayOfWeek
    Select Case sStr
        Case "Monday" : eDay = DayOfWeek.Monday
        Case "Tuesday" : eDay = DayOfWeek.Tuesday
            '...
    End Select

No, this sounds too dumb. We can use the built-in shared function Parse of the System.Enum class. Method accepts two parameters, the type of enumeration and the actual value being parsed.

eDay = System.Enum.Parse(GetType(System.DayOfWeek), "Monday")

This only works with option strict set to off, because Enum.Parse() returns a System.Object value. So when using option strict set to on, the code gets more obfuscated:

eDay = CType( _ 
          System.Enum.Parse(GetType(System.DayOfWeek), "Monday"),  _ 
          System.DayOfWeek)

Anyway, now we have the value! :)

String.Concat instead of &

We all know that the concatenation operator & is evil when you use it intensively.
We know writing like this is no good:

Dim s as String = ""
s &= "<"
s &= spFieldName
s &= ">"
s &= spFieldValue
s &= "</"
s &= spFieldName
s &= ">"

This is no good because a new String object is being created for every line of this code and the value is being copied to the newly created object and no optimizations can be made during compilation. One could avoid this using a StringBuilder object and calling ToString() method to achieve the same result. However the readability wouldn’t be too good.

So today I was paging through code of some library not written by me. I used Lutz Roeder’s Reflector. I came accross the following approach of string concatenation:

Dim s as String
s = String.Concat("<",  spFieldName,  ">", spFieldValue, "</", spFieldName, ">")

They had used the “Dotfuscator” to obfuscate the code of the library. As far as I know, the Reflector also makes some optimizations to the code. That’s why I’m not really sure if this is a coding approach or an effect by Dotfuscator/Reflector. Anyway, I believe it is quite a good solution: it doesn’t use much memory for execution and it’s quite readable!

Stulbas beigas?

Piemērs no dzīves: ko dara nekārtīgs programmētājs, ja vajadzīga funkcija, kas “sakabina” kopā divus faila pilnās takas fragmentus? Raksta funkciju

Function getFullPath(ByVal spRoot As String, ByVal spSub As String) As String
    Return spRoot & "/" & spSub
End Function

Viss būtu labi, bet gadījumos , ja spRoot jau beidzas ar “/” simbolu, izveidosies neglīta taka ar divām slīpsvītrām pēc kārtas. Tāpēc kārtīgāks programmētājs cenšas notīrīt liekos “/” no Root beigām:

Function getFullPath(ByVal spRoot As String, ByVal spSub As String) As String
    'korekti ir nemainīt parametra vērtību funkcijā - nokopēsim
    Dim sRoot As String = spRoot

    'kamēr vien rinda beidzas ar /, dzēšam tos nost
    While Right(sRoot, 1) = "/"
        'ja būs atkārtoti / beigās, notīrīs visus
        sRoot = Left(spRoot, Len(sRoot) - 1)
    End While

    Return sRoot & "/" & spSub
End Function

Ir jau labi, tikai darba daudz. Un vispār – kāpēc tā darīt, ja ir izveidota String klases funkcija TrimEnd. Lietojot to, varam atkal funkcijas būtību ierakstīt vienā rindiņā:

Function getFullPath(ByVal spRoot As String, ByVal spSub As String) As String
    Return spRoot.TrimEnd("/"c) & "/" & spSub
End Function

TrimEnd un TrimStart funkcijām parametros var nodot sarakstu ar dažādiem simboliem, kurus mēģināt nodzēst no rindas beigām. Piemēram, ja gribam pārliecināties, ka apakšdirektorijas daļa spSub patiesībā nav līdzīga šai “../../../../windows/system32/”, varam nodzēst arī punkta simbolus no otrā parametra abu parametru savienojamajiem galiem.

Function getFullPath(ByVal spRoot As String, ByVal spSub As String) As String
    Return spRoot.TrimEnd("/"c, "/"c) & "/" & spSub.TrimStart("."c, "/"c)
End Function

Cīņa par drošu kodu? Nu, tā laikam būs.

P.S. Uzmanīgi, ja kāds no parametriem būs Nothing, funkcija metīs kļūdu!

Mainīgo deklarēšana

Starp citu … VB6 skaitījās sliktais stils rakstīt šādi:

Dim i, j As Integer

Šādas deklarācijas rezultātā i bija ar tipu Variant, bet j bija Integer (nevis kā varētu šķist, abi Integer).

Toties VB.net šī pieeja ir tiek atbalstīta un saprasta tieši tā, kā tā izskatās.

MSDN mazliet mānās par ASP.Net failu upload

Mēģinām augšuplādēt (upload) failu ASP.net lapā. Priekš tam lapas kodā jāievieto apmēram šāds:

<input id="myFile" type="file" runat="server" />
<input type=button id="cmdUp" runat="server" />

Pēc tam varam ķert notikumu, kad nospiesta upload poga. Pēc MSDN rādītā piemēra, pietiek pārbaudīt vai UploadedFile atribūts nav nothing, tas ir, šādi:

Private Sub Uploads (ByVal s As System.Object, ByVal e As System.EventArgs) Handles cmdUp.ServerClick
         If  Not Me.myFile.PostedFile Is Nothing Then
        '... darām kaut ko
        End If
End Sub

Tomēr patiesībā “Not Me.myFile.PostedFile is Nothing” vienmēr atgriež True – pat tad, ja faila nav. Vismaz pie konfigurācijas, kāda tā ir man. Līdz ar to vienīgais veids, kā pārbaudīt, ka tiešām kaut kas ir uploadēts, ir:

Private Sub Uploads (ByVal s As System.Object, ByVal e As System.EventArgs) Handles cmdUp.ServerClick
         If  (Not Me.myFile.PostedFile Is Nothing) AndAlso _ 
            (Me.myFile.PostedFile.ContentLength>0) Then
        '... darām kaut ko
        End If
End Sub

Tā, redz!