StyleCop

15 December 2008

Tuli kokeiltua StyleCop:ia paremman koodin luettavuuden nimissä. StyleCop on C# kielisen lähdekoodin tyyli tarkastaja.  Lopputuloksena on että noin 230 rivin lähdekoodista tuli pikkasen yli 100 varoitusta.  Voidaankin todeta että ohjelma on pikkutarkka. Toistaalta kuka noita yli 100 sivun tyyli oppaita kukaan ulkoa rupeaa muistelmaan joten tämmöinen työkalu on ihan kätevä.

Elikkä nopeana yhteenveto voidaan pitää että muuttujien nimet pitäisi alkaa pienellä alkukirjaimella, ei alleviivausta, this osoitinta pitäisi muistaa käyttää ja kommentteja kirjoittaa.

Ohjelma integroituu mutkattomasti Visual Studioon. Jos ei halua väsätä generoitua koodia tyylisääntöjen mukaiseksi voi tiedoston alkuun liittää // <auto-generated /> tagin.

Visual Studio 2010

25 November 2008

Visual studio 2010 ja .NET framework 4.0 on saatavilla teknologia demo täältä.

Harjoitus paketti on saatavissa täältä.

C# merkkijono suorituskyky testi

13 August 2008

Tässä on pieni suorituskyky testi C#:llä, joka vertailee merkkijonojenen merkkien käsittelyä eri toimintatavoilla.

Testin tulokset:

28218,75 ns per operation (RegEx)
765,63 ns per operation (StringBuilder)
687,50 ns per operation (char taulukot)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace Testi1
{
    class Program
    {
        static void Main(string[] args)
        {
            int loopCount = 1000000;
            long startTime, endTime;
            double nanoseconds;
 
            startTime = DateTime.Now.Ticks * 100;
            for (int x = 0; x &lt; loopCount; x++)
            {
                ExtractNumbers1("Its is only 4 numbers but must be 1337");
            }
            endTime = DateTime.Now.Ticks * 100;
            nanoseconds = ((double)(endTime - startTime)) / ((double)loopCount);
            Console.WriteLine(nanoseconds.ToString("F") + " ns per operation");
 
            startTime = DateTime.Now.Ticks * 100;
            for (int x = 0; x &lt; loopCount; x++)
            {
                ExtractNumbers2("Its is only 4 numbers but must be 1337");
            }
            endTime = DateTime.Now.Ticks * 100;
            nanoseconds = ((double)(endTime - startTime)) / ((double)loopCount);
            Console.WriteLine(nanoseconds.ToString("F") + " ns per operation");
 
            startTime = DateTime.Now.Ticks * 100;
            for (int x = 0; x &lt; loopCount; x++)
            {
                ExtractNumbers3("Its is only 4 numbers but must be 1337");
            }
            endTime = DateTime.Now.Ticks * 100;
            nanoseconds = ((double)(endTime - startTime)) / ((double)loopCount);
            Console.WriteLine(nanoseconds.ToString("F") + " ns per operation");
 
            Console.Read();
        }
 
        static string ExtractNumbers1(string expr)
        {
            return string.Join(null, System.Text.RegularExpressions.Regex.Split(expr, "[^\\d]"));
        }
 
        static string ExtractNumbers2(string expr)
        {
            StringBuilder sb = new StringBuilder(expr.Length);
 
            foreach (char ch in expr)
            {
                if (Char.IsDigit(ch))
                    sb.Append(ch);
            }
 
            return sb.ToString();
        }
 
        static string ExtractNumbers3(string expr)
        {
            char[] nums = new char[expr.Length];
            int pos = 0;
 
            foreach (char ch in expr)
            {
                if( Char.IsDigit( ch ))
                    nums[pos++] = ch;
            }
 
            return new string ( nums, 0, pos );
        }
    }
}

C# Laajennetut luokat

24 July 2008

Tuli luettua C# kielioppia. Mukavana ominaisuutena huomasin että nykyään pystytään (versiosta 3.0 lähtien) laajentamaan valmiita kirjastoluokkia. Tämä avaa mukavia mahdollisuuksia poistaa itseään häiritseviä piirteitä.

Laajennuksen tekeminen onnistuu lisäämällä this sana metodin parametrin määrittelyyn. Alla esimerkki string luokan laajennuksesta.

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Text.RegularExpressions;
 
namespace StringExtensions
{
    public static class StringExtensions
    {
        /// <summary>
        /// Remove any non-numeric characters and then return the resultant string.
        /// Useful for parsing phone numbers.
        /// </summary>
        /// <param name="s">The text that is to be evaluated.</param>
        /// <returns>New string</returns>                 
        public static string RemoveNonNumeric(this string s)
        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < s.Length; i++)
                if (Char.IsNumber(s[i]))
                    sb.Append(s[i]);
            return sb.ToString();
        }
 
        /// <summary>
        /// Returns the substring of the first argument string that follows the first occurrence 
        /// of the second argument string in the first argument string, or the empty 
        /// string if the first argument string does not contain the second argument string.
        /// </summary>
        /// <param name="source">The text that is to be evaluated.</param>
        /// <param name="value">String to search.</param>
        /// <returns>New string</returns>
        public static string SubstringAfter(this string source, string value)
        {
            if (string.IsNullOrEmpty(value))
            {
                return source;
            }
            CompareInfo compareInfo = CultureInfo.InvariantCulture.CompareInfo;
            int index = compareInfo.IndexOf(source, value, CompareOptions.Ordinal);
            if (index < 0)
            {
                //No such substring
                return string.Empty;
            }
            return source.Substring(index + value.Length);
        }
 
        /// <summary>
        /// Returns the substring of the first argument string that precedes the first occurrence of 
        /// the second argument string in the first argument string, or the empty string if 
        /// the first argument string does not contain the second argument string.
        /// </summary>
        /// <param name="source">The text that is to be evaluated.</param>
        /// <param name="value">String to search.</param>
        /// <returns>New string</returns>
        public static string SubstringBefore(this string source, string value)
        {
            if (string.IsNullOrEmpty(value))
            {
                return value;
            }
            CompareInfo compareInfo = CultureInfo.InvariantCulture.CompareInfo;
            int index = compareInfo.IndexOf(source, value, CompareOptions.Ordinal);
            if (index < 0)
            {
                //No such substring
                return string.Empty;
            }
            return source.Substring(0, index);
        }
 
        /// <summary>
        /// Truncate string using dots...
        /// Useful for displaying long filename paths.
        /// </summary>
        /// <param name="s">String to be truncated.</param>
        /// <param name="maxLength">Maximum string size</param>
        /// <returns>New string</returns>
        public static string Truncate(this string s, int maxLength)
        {
            if (string.IsNullOrEmpty(s) || maxLength <= 0)
                return string.Empty;
            else if (s.Length > maxLength)
                return s.Substring(0, maxLength) + "...";
            else
                return s;
        }
 
        /// <summary>
        /// Reverse string.
        /// </summary>
        /// <param name="s">String to be reversed.</param>
        /// <returns>Reversed string.</returns>
        public static string Reverse(this string s)
        {
            char[] c = s.ToCharArray();
            Array.Reverse(c);
            return new string(c);
        }
 
        /// <summary>
        /// Test if string contains only numbers.        
        /// </summary>
        /// <param name="text">The text that is to be evaluated.</param>
        /// <returns>True if text included only letters.</returns>
        public static bool IsAlpha(this string text)  
        {            
            foreach (char c in text.ToLower())  
            {  
                if (!char.IsLetter(c))
                    return false;  
            }  
            return true;  
        }
 
        /// <summary>
        /// Check is string is valid email address.
        /// </summary>
        /// <param name="email">Email to be checked.</param>
        /// <returns>True if email address was ok.</returns>        
        public static bool IsValidEmailAddress(this string email)
         {             
             const string REGEX_VALID_EMAIL =
                 @"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|" +
                 @"(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$";
             bool valid = Regex.IsMatch(email, REGEX_VALID_EMAIL);
             return valid;
         }
 
        /// <summary>
        /// Convert string to integer.
        /// </summary>
        /// <param name="s">String to be processed.</param>
        /// <returns>Integer</returns>
        public static int ToInteger(this string s)
        {
            int integerValue = 0;
            int.TryParse(s, out integerValue);
            return integerValue;
        }
 
        /// <summary>
        /// Compare two strings, ignore case.
        /// </summary>
        /// <param name="s1">String 1</param>
        /// <param name="s2">String 2</param>
        /// <returns>If equal return true.</returns>
        public static bool Comparei(this string s1, string s2)
        {
            CompareInfo Compare = CultureInfo.InvariantCulture.CompareInfo;
            int i = Compare.IndexOf(s1, s2, CompareOptions.IgnoreCase);
            if (i != 0 )
                return true;
            else
                return false;
        }
    }
}

Herätyskello

19 July 2008

Joskus se pyörä vaan kannattaa keksiä uudestaan. Tämmöinen tapaus tuli eteen kun koitin etsiä sopivaa herätyskello ohjelmaa. No mitäs ongelmaa tässä voi olla eikun surffailua www.download.com:iin ja alarm hakusanaksi. Ongelmaksi muodostui 739 osumaa. Eri ohjelmien vertailu vie ikuisuuden.

Herätyskello on ohjelmoinnin kannalta helppo tehtävä. Tuntuu että jokainen harrastelija on väsännyt oman ohjelman jossa on miljoona eri ominaisuutta ja minä olisin kaivannut vain jotain pientä ja yksinkertaista.

Eli mahdollisimman minimalistinen toteutus. Formi on mahdollisimman pieni ettei se vie ruudulta tilaa. Eli luodaan pieni työkaluikkuna. Erilaisia formin asetuksia:

  • BackColor 233;233;233
  • FormBorderStyle FixedToolWindow
  • MaximizeBox False
  • MaximumSize 156;70
  • Opacity 80%
  • ShowInTaskBar False

Kuten kuvasta näkyy on käytössä on kaksi lapsi ikkunaa. DateTimePicker ja CheckBox. DateTimePickerin asetuksia on yksinkertaistettu, tiputin sekunnit ja kalenteri mahdollisuuden pois.

  • CustomFormat HH:mm
  • Format Custom
  • ShowUpDown True

Valintalaatikko joka näyttää napilta on valittu Appearance: button kohdasta. Näin saadaan aika minimalistinen ulkomuoto. Yleensä herätyskellot pitää jonkinlaista ääntä mutta itse pidän enemmän visuaalisista tehosteista. Ikkuna pomppaa eteen hälyyttäessä ja värisee (Windows live messangerin tapaan).

// Simple alarm clock.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
 
namespace Alarm
{
    public partial class Alarm : Form
    {
        private DateTime AlarmTime;
        private bool AlarmState = false;
 
        public Alarm()
        {
            InitializeComponent();
        }
 
        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            // If alarm is running then change it to back to normal.
            if (AlarmState == true)
            {
                AlarmState = false;
                checkBox1.Text = "Set alarm";
                TopMost = false;
                Timer.Interval = 60000;
                Timer.Stop();
                return;
            }
 
            if (checkBox1.Checked == false)
            {
                Timer.Stop();
                dateTimePicker1.Enabled = true;
                return;
            }
 
            AlarmTime = dateTimePicker1.Value;
            RolloverTime();
 
            Timer.Start();
            dateTimePicker1.Enabled = false;
        }
 
        private void RolloverTime()
        {
            // If the user selects a time already passed, it must be for tomorrow
            if (DateTime.Now.TimeOfDay.CompareTo(AlarmTime.TimeOfDay) &gt; 0)
            {
                AlarmTime = new DateTime(DateTime.Now.Year,
                    DateTime.Now.Month, DateTime.Now.Day + 1,
                    AlarmTime.Hour, AlarmTime.Minute, AlarmTime.Second);
            }
            // Otherwise, set it for today
            else
            {
                AlarmTime = new DateTime(DateTime.Now.Year,
                    DateTime.Now.Month, DateTime.Now.Day,
                    AlarmTime.Hour, AlarmTime.Minute, DateTime.Now.Second);
            }
        }
 
        private void AlarmTimer_Tick(object sender, EventArgs e)
        {
            AlarmState = true;
            checkBox1.Text = "Turn off";
            TopMost = true;
 
            NudgeMe(this);
            Timer.Interval = 5000;
            Timer.Start();
 
        }
 
        private void Alarm_Load(object sender, EventArgs e)
        {
            dateTimePicker1.Value = dateTimePicker1.Value.AddMinutes(10);
        }
 
        static void NudgeMe(Form TargetForm)
        {
            // Store the original location of the form.
 
            int xCoord = TargetForm.Left;
            int yCoord = TargetForm.Top;
 
            // An integer for storing the random number each time
 
            int rnd = 0;
 
            // Instantiate the random generation mechanism
 
            Random RandomClass = new Random();
 
            for (int i = 0; i &lt;= 500; i++)
            {
                rnd = RandomClass.Next(xCoord + 1, xCoord + 15);
                TargetForm.Left = rnd;
                rnd = RandomClass.Next(yCoord + 1, yCoord + 15);
                TargetForm.Top = rnd;
            }
 
            // Restore the original location of the form
            TargetForm.Left = xCoord;
            TargetForm.Top = yCoord;
        }
    }
}

Koodin väritystä WordPressissä

23 June 2008

Löysin blogiin WP-Syntax lisäosan, se värittää liitetyn koodin käyttäen GeSHi palikkaa. Hyvänä puolena on että se tukee huimaa joukkoa erilaisia kieliä.

No liitetään tähän pieni C# esimerkki koodi, joka ihmettelee annettua teksti tiedostoa.

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
using System;
using System.Text;
using System.IO;
 
namespace cnt_wrd
{
    class Program
    {
        static int[] freqtbl = new int [256];
 
        static int Main(string[] args)
        {
            FileStream istream;
 
            if (args.Length == 0)
            {
                Console.WriteLine("Use: cnt_wrd file.txt\n");
                return 0;
            }
            try
            {
                istream = new FileStream(args[0], FileMode.Open, FileAccess.Read);
            }
            catch (Exception)
            {
                Console.WriteLine("Could not open for reading.");
                return -1;
            }
 
            StreamReader reader = new StreamReader(istream);
            StringBuilder strb = new StringBuilder();
 
            // Counter for the stats
            int Lines = 0;
            int Words = 0;
            int Chars = 0;
            int Sentences = 0;
 
            while (reader.Peek() > 0)
            {
                ++Lines;
                strb.Length = 0;
                string str = reader.ReadLine();
                Chars += str.Length;
                Sentences = SentencesCount(str);
                Words += Count(str);
            }
            istream.Close();
 
            for (int i = 0; i < freqtbl.Length; i++)
            {
                if (freqtbl[i] == 0)
                    continue;
                Console.Write("{0} {1}\t", Convert.ToChar(i), freqtbl[i]);
            }
            Console.WriteLine("\nLines: {0}, Sentences:{1}, Words: {2}, Chars: {3}",
                    Lines, Sentences, Words, Chars);
            return (0);
        }
 
        static int SentencesCount(string str)
        {
            int cnt = 0;
 
            for (int i = 1; i < str.Length; i++)
            {
                if (str[i] == '.')
                    cnt++;
                if (str[i] == '?')
                    cnt++;
                if (str[i] == '!')
                    cnt++;
            }
            if (cnt == 0)
                cnt = 1;
 
            return cnt;
        }
 
        static int Count(string str)
        {
            int c = 0;
 
            for (int i = 1; i < str.Length; i++)
            {
                if (char.IsWhiteSpace(str[i - 1]) == true)
                {
                    if (char.IsLetterOrDigit(str[i]) == true ||
                        char.IsPunctuation(str[i]))
                    {
                        c++;
                        freqtbl[str[i]]++;
                    }
                }
            }
            if (str.Length > 2)
            {
                c++;
            }
            return c;
        }
    }
}