<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="https://www.dotnetzone.gr:443/cs/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>.NET Framework</title><link>https://www.dotnetzone.gr:443/cs/forums/14/ShowForum.aspx</link><description>Θέματα για threading, remoting, reflection, exception handling, security, regex κλπ.</description><dc:language>el</dc:language><generator>CommunityServer 2.1 SP3 (Build: 20423.1)</generator><item><title>Απ: C# ή VB.NET ?</title><link>https://www.dotnetzone.gr:443/cs/forums/thread/71746.aspx</link><pubDate>Thu, 13 Dec 2012 20:44:21 GMT</pubDate><guid isPermaLink="false">2622095e-976c-431a-859e-16783ec7ecd7:71746</guid><dc:creator>sakalis</dc:creator><slash:comments>0</slash:comments><comments>https://www.dotnetzone.gr:443/cs/forums/thread/71746.aspx</comments><wfw:commentRss>https://www.dotnetzone.gr:443/cs/forums/commentrss.aspx?SectionID=14&amp;PostID=71746</wfw:commentRss><description>&lt;BLOCKQUOTE&gt;&lt;div&gt;&lt;img src="http://www.dotnetzone.gr/cs/Themes/default/images/icon-quote.gif"&gt; &lt;strong&gt;BruteForce:&lt;/strong&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;br&gt;&lt;div&gt;Σίγουρα κάνει pay back το testing. Απλά μερικοί άνθρωποι δεν νοιάζονται για τις μετέπειτα συνέπειες επειδή πολύ απλά δεν σκοπεύουν να είναι στην εταιρία όταν θα σκάσουν αυτές οι συνέπειες. Άρα νοιάζονται μόνο για τα short term benefits.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;Αυτόν τον παράγοντα ελάχιστοι τον σκέφτονται ;-)&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/BLOCKQUOTE&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Το θέμα δεν ειναι μόνο οι συνέπειες στο μέλλον. ΤΟ θέμα ειναι ότι με τα unit test μειώνεις και τον χρόνο του development, μειώνεις τα ξενύχτια στο τέλος του deadline μειώνεις το άγχος κτλ κτλ. Επομένως οι παραπάνω δεν νοιάζονται ούτε για τα short term benefits&lt;/div&gt;</description></item><item><title>Απ: C# ή VB.NET ?</title><link>https://www.dotnetzone.gr:443/cs/forums/thread/71734.aspx</link><pubDate>Tue, 11 Dec 2012 23:06:08 GMT</pubDate><guid isPermaLink="false">2622095e-976c-431a-859e-16783ec7ecd7:71734</guid><dc:creator>Tsopi</dc:creator><slash:comments>0</slash:comments><comments>https://www.dotnetzone.gr:443/cs/forums/thread/71734.aspx</comments><wfw:commentRss>https://www.dotnetzone.gr:443/cs/forums/commentrss.aspx?SectionID=14&amp;PostID=71734</wfw:commentRss><description>Ok, συμφωνώ μαζί σου. Όμως όταν έχω χιλιοπαιδευτεί όπως σου είπα πιο κάτω για ένα backup αρχείο, πραγματικά δεν έχω όρεξη να ασχοληθώ με κάτι σχεδόν απίθανο και για εμένα ασύμαντο. Μιλάμε για την συνάρτηση που εμφανίζει τον χρόνο αναπαραγωγής! Δηαδή το να πάρεις ένα seconds σε integer&amp;nbsp; και να το μετατρέψεις σε ταμπελίτσα του στυλ&amp;nbsp; 12:30 . Ο κώδικας είναι τόσο απλός και καθαρός,&amp;nbsp; τα datatypes είναι ηλιθιοδώς μεγάλα ( έχω βάλει int64 για τα seconds) που για κάνει ένα overflow πρέπει να το χρησιμοποιήσει ο τσάκ νόρις. Οκ, να βάλω να ελέγχει το πρόβλημα...αλλά ποιο πρόβλημα ακριβώς να πιάσω?&lt;br&gt;&lt;br&gt;Να βάλω Try Catch και να ελένξω τι? Να πέσει αρνητικός αριθμός? Παρόλο που έχω βάλει int64 και οχι uint64 ( μπας και συμβεί ποτέ κάτι), αυτό σημαίνει πως κάτι στο windows media player δεν λειτουργεί σωστά ( επειδή χρησιμοποιώ αυτό το component ) . Και τι υποτήθεται πως πρέπει να κάνω για αυτό? Πως να αναπαραγάγω αυτό το πρόβλημα για να δώ πως να το αντιμετωπίσω? &lt;br&gt;&lt;br&gt;Να ελένξω το overflow? Δηλαδή οκ , αν κάνεις overflow σε int64 μιλάμε πως έχεις βάλει τραγούδι να παίζει για &lt;span class="cwcot" id="cwos"&gt;292271023045 χρόνια! ok... δεν με νοιάζει... πρόβλημα του χρήστη!&lt;br&gt;&lt;/span&gt;</description></item><item><title>Απ: C# ή VB.NET ?</title><link>https://www.dotnetzone.gr:443/cs/forums/thread/71733.aspx</link><pubDate>Tue, 11 Dec 2012 20:31:34 GMT</pubDate><guid isPermaLink="false">2622095e-976c-431a-859e-16783ec7ecd7:71733</guid><dc:creator>BruteForce</dc:creator><slash:comments>0</slash:comments><comments>https://www.dotnetzone.gr:443/cs/forums/thread/71733.aspx</comments><wfw:commentRss>https://www.dotnetzone.gr:443/cs/forums/commentrss.aspx?SectionID=14&amp;PostID=71733</wfw:commentRss><description>Tα code contracts... άλλο ένα syntactic sugar της MS. Το μισό .ΝΕΤ είναι syntactic sugar τελικά. Ναι αυτή είναι η λογική με αυτά τα contracts. Να ελέγχεις τα pre/post conditions.&lt;div&gt;&lt;br&gt;&lt;div&gt;Το Test Driven Development στην πράξη είναι δύσκολο, γιατί όταν η κλάση δεν υπάρχει, δεν ξέρεις και τι κάνει, οπότε πώς θα την τεστάρεις;;;&lt;br&gt;Εγώ έχω εφαρμόσει αυτό που ονομάζω "Test Targeted Development", δηλαδή development που φτιάχνει testable modules. Και φυσικά μπαίνω στον κόπο να γράψω και τα tests ή να βάζω άλλους να τα γράψουν. Τώρα που κάνω κουμάντο εγώ, το testing είναι βασικό κομμάτι του development, όχι μία προαιρετική δραστηριότητα.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Σίγουρα κάνει pay back το testing. Απλά μερικοί άνθρωποι δεν νοιάζονται για τις μετέπειτα συνέπειες επειδή πολύ απλά δεν σκοπεύουν να είναι στην εταιρία όταν θα σκάσουν αυτές οι συνέπειες. Άρα νοιάζονται μόνο για τα short term benefits.&lt;/div&gt;&lt;div&gt;Αυτόν τον παράγοντα ελάχιστοι τον σκέφτονται ;-)&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Θα σου γράψω άλλη ώρα για τα static.&lt;/div&gt;</description></item><item><title>Απ: C# ή VB.NET ?</title><link>https://www.dotnetzone.gr:443/cs/forums/thread/71730.aspx</link><pubDate>Tue, 11 Dec 2012 08:42:55 GMT</pubDate><guid isPermaLink="false">2622095e-976c-431a-859e-16783ec7ecd7:71730</guid><dc:creator>xabikos</dc:creator><slash:comments>0</slash:comments><comments>https://www.dotnetzone.gr:443/cs/forums/thread/71730.aspx</comments><wfw:commentRss>https://www.dotnetzone.gr:443/cs/forums/commentrss.aspx?SectionID=14&amp;PostID=71730</wfw:commentRss><description>Αρχικά να σε ευχαριστήσω για τον κόπο σου μιας και εγώ ζήτησα το παράδειγμα για να καταλάβω τι ακριβώς εννοείς. &lt;img src="http://www.dotnetzone.gr/cs/emoticons/emotion-1.gif" alt="Smile" /&gt;&lt;br&gt;Δουλεύω ως επαγγελματίας .ΝΕΤ developer 3+ χρόνια οπότε έχω πολλά να μάθω ακόμη και κάθε συμβουλή από ποιο έμπειρους συναδέλφους είναι πολύτιμη και προσπαθώ να την καταλάβω και να την εκτιμήσω. &lt;br&gt;&lt;br&gt;Το συγκεκριμένο παράδειγμα είναι εκτενές και πλήρες και έπιασα το βασικό νόημα, αν και είναι δύσκολο να το ακολουθήσω πλήρως μιας και δεν έχω καθόλου εμπειρία με T-SQL. Έχω δουλέψει μόνο με ORMs οπότε δεν υπήρξε ανάγκη για SQL μέχρι τώρα. Ξέρω ότι πρέπει να γνωρίζω και SQL ως προγραμματιστής αλλά αυτό είναι θέμα για άλλη συζήτηση μάλλον. &lt;br&gt;&lt;br&gt;Για να σχολιάσω το παράδειγμα απλά μου φαίνεται λογικό να θέλει πάρα πολλούς ελέγχους το συγκεκριμένο μιας και είναι ειδική περίπτωση. Σε περίπτωση που είχαμε C# κώδικα και αφού υποθέσουμε ότι ακολουθούμε τις γνωστές αρχές ότι κάθε μέθοδος πρέπει να είναι υπεύθυνη μόνο για μία λειτουργία και συνήθως λίγες γραμμές κώδικα τότε ένας παρόμοιος έλεγχος δεν είναι πολύ δύσκολος και μπορούμε να εκμεταλλευτούμε τα &lt;a href="http://msdn.microsoft.com/en-us/library/dd264808.aspx"&gt;code contracts&lt;/a&gt; που υπάρχουν γι αυτήν ακριβώς την δουλειά. Επίσης αν υποθέσουμε ότι κάνουμε Test Driven Development τότε θα πρέπει πρώτα να γράφουμε όλα τα unit tests που εννοείται περικλείουν όλες τις πιθανές περιπτώσεις και με exceptions και στην συνέχεια γράφουμε τον κώδικα για να περάσουν αυτά τα τεστ. Βέβαια και πάλι να γράψουμε τεστ για όλες τις περιπτώσεις είναι δύσκολο και συνήθως χρονοβόρο κάτι που δεν είναι αποδεκτό από το project management αν και είμαι πεπεισμένος πως κάνει pay back ειδική στην συντήρηση του κώδικα από ανθρώπους που δεν τον έγραψαν.&lt;br&gt;&lt;br&gt;Αυτό που θα είχε μεγαλύτερο ενδιαφέρον για μένα θα ήταν ένα παράδειγμα σχετικά με τις static functions σε C# και πως αυτό θα μπορούσε να κάνει τον κώδικα μας πιο ασφαλή. Είναι μια τεχνική που δεν μπόρεσα να σκεφτώ πως λειτουργεί. Αλλά δεν μπορώ να ζητήσω ξανά κάτι μιας και ήδη έδωσες ένα παράδειγμα για το τι εννοούσες. &lt;br&gt;</description></item><item><title>Απ: C# ή VB.NET ?</title><link>https://www.dotnetzone.gr:443/cs/forums/thread/71726.aspx</link><pubDate>Tue, 11 Dec 2012 00:09:02 GMT</pubDate><guid isPermaLink="false">2622095e-976c-431a-859e-16783ec7ecd7:71726</guid><dc:creator>BruteForce</dc:creator><slash:comments>0</slash:comments><comments>https://www.dotnetzone.gr:443/cs/forums/thread/71726.aspx</comments><wfw:commentRss>https://www.dotnetzone.gr:443/cs/forums/commentrss.aspx?SectionID=14&amp;PostID=71726</wfw:commentRss><description>&lt;div&gt;Κάποιοι στην κουβέντα ζητήσανε παραδείγματα.&lt;/div&gt;&lt;div&gt;Επειδή το συγκεκριμένο που ζητήθηκε θεωρώ ότι είναι πολύ απλό και αυτονόητο, σας δίνω ένα παράδειγμα error checking/handling σε κώδικα T-SQL.&lt;/div&gt;&lt;div&gt;Και από αυτό φανταστείτε τι γίνεται στην C#.&lt;/div&gt;&lt;div&gt;Απλά αντί για RAISERROR πέφτουν βροχή τα throw InvalidParameterException και όταν ο caller έχει κάπου bug και κάποια στιγμή κάνει invalid call το πιάνουμε αμέσως και το λύνουμε σε 30 δευτερόλεπτα.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Η τακτική που ακολουθώ πάντα είναι ότι ο έλεγχος των παραμέτρων είναι &lt;u&gt;εξαντλητικός &lt;/u&gt;σε όλα τα "Boundaries".&lt;/div&gt;&lt;div&gt;Όταν ο κώδικας C# καλεί κώδικα SQL, τότε περνάμε ένα boundary, άρα θέλει εξαντλητικό έλεγχο.&lt;/div&gt;&lt;div&gt;Οι public methods μιας κλάσης είναι boundary.&lt;/div&gt;&lt;div&gt;Άρα θέλει εξαντλητικό έλεγχο και εκεί, κοκ.&lt;/div&gt;&lt;div&gt;Στις άλλες περιπτώσεις γίνεται απλός έλεγχος παραμέτρων με βάση την κοινή λογική και με ολίγη από Debug.Assert.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Όπως βλέπετε παρακάτω, πέρα από τις παραμέτρους, και ΚΑΘΕ statement SQL που πειράζει τη βάση ελέγχεται και βγαίνει custom μήνυμα λάθους ώστε να έχω πλήρη πληροφόρηση για το τι παίχτηκε.&lt;/div&gt;&lt;div&gt;Θα μπορούσα με BEGIN TRY να τα κάνω στον μισό κώδικα, αλλά μετά θα έπαιρνα ότι error message προαιρείται ο SQL server και θα ψαχνόμουν στο άπειρο.&lt;/div&gt;&lt;div&gt;Και σιγά μην μου έβαζε μέσα στο μήνυμα το ProcessId ή άλλες χρήσιμες πληροφορίες που μπορεί να θέλω.&lt;/div&gt;&lt;div&gt;Δεν χρησιμοποιώ BEGIN TRY, παρά μόνο στα upgrade scripts των βάσεων.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Μου χάλασε λίγο τη στοίχηση το copy paste, αλλά μπορείτε να δείτε ότι όλα τα error checking είναι ένα tab πιο μέσα, ώστε να μην μπερδεύονται με τον υπόλοιπο κώδικα.&lt;/div&gt;&lt;div&gt;Επίσης το section με τα αρχικά parameter checks ξεχωρίζει από το main body με σχόλια, ώστε γρήγορα να βρίσκω αυτό που θέλω.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Όπως βλέπετε έχει πολύ κόπο το πλήρες error checking/handling και θέλει πολύ υπομονή.&lt;/div&gt;&lt;div&gt;Αλλά κάθε βράδυ κοιμάμαι ύσηχος :-)&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Α ναι, και μην ξεχνάτε να ελέγχετε στις stored procedures αν υπάρχει transaction :-)&lt;/div&gt;&lt;div&gt;Το τι γίνεται όταν ο caller "ξεχάσει" να ξεκινήσει transaction και σκάσει κάποιο λάθος στην SQL έχει πάρα πολύ "πλάκα".&lt;/div&gt;&lt;div&gt;Είναι από τα αγαπημένα μου horror movies.&lt;/div&gt;&lt;div&gt;Γι'αυτό και το συγκεκριμένο είναι και ένα από τα αυτοματοποιημένα μας test.&lt;/div&gt;&lt;div&gt;Enumerate all strprocs και κλήση τους με όλες τις παραμέτρους null και χωρίς transaction.&lt;/div&gt;&lt;div&gt;Αν δεν γίνει fail με το σωστό error message, τότε έχουμε bug.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Επίσης παρατηρήστε τη χρήση των "database enums" ώστε ο κώδικας να είναι και περισσότερο αναγνώσιμος.&lt;/div&gt;&lt;div&gt;Είναι απλές SQL Functions του ενός statement. Η μία return 1, η άλλη return 2, κ.ο.κ.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Κάντε μου και ένα free code review μήπως υπάρχει και κανα bug ;-)&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;p&gt;&lt;pre&gt;CREATE PROCEDURE dbo.InsertAction&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;@a_ProcessId&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;int,&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;@a_ActionKindId&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;int,&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;@a_NewActionId&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;int OUTPUT&lt;br&gt;WITH ENCRYPTION AS&lt;br&gt;BEGIN&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;SET NOCOUNT ON&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;br&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;/***************************************************&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;					&lt;/span&gt;Parameter Validations&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt; ***************************************************/&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;-- Make sure everything is transactional no matter how we were called&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;IF (@@TRANCOUNT = 0)&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;BEGIN&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;RAISERROR('Invalid call. Transaction required', 11, 0) WITH SETERROR&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;RETURN&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;END&lt;/pre&gt;&lt;pre&gt;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;--------------------------&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;-- Check for NULLs&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;--------------------------&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;IF ((@a_ProcessId IS NULL) OR (@a_ActionKindId IS NULL))&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;BEGIN&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;RAISERROR('Invalid parameter. Process or ActionKind Id is NULL', 11, 0) WITH SETERROR&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;RETURN&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;END&lt;/pre&gt;&lt;pre&gt;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;-- Check that the process exists and is not cancelled or completed&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;DECLARE @ProcessState tinyint&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;SELECT @ProcessState = ProcessState&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;FROM Processes WITH (UPDLOCK, ROWLOCK)&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;WHERE (ID = @a_ProcessId)&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;br&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;IF (@@ROWCOUNT = 0)&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;BEGIN&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;-- We checked the Process Id is non-null so this means it is invalid.&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;RAISERROR('Invalid parameter. Process %d does not exist', 11, 0, @a_ProcessId) WITH SETERROR&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;RETURN&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;END&lt;/pre&gt;&lt;pre&gt;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;IF (@ProcessState IN (dbo.ProcessState_Completed(), dbo.ProcessState_Cancelled()))&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;BEGIN&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;RAISERROR('Process %d is not in a state that allows actions to be inserted.', 11, 0, @a_ProcessId) WITH SETERROR&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;RETURN&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;END&lt;/pre&gt;&lt;pre&gt;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;-- Make sure the process is locked&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;IF (dbo.fn_ProcessIsLocked(@a_ProcessId) = 0)&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;BEGIN&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;RAISERROR('Process %d is not locked.', 11, 0, @a_ProcessId) WITH SETERROR&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;RETURN&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;END&lt;/pre&gt;&lt;pre&gt;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;-- Pick up the stuff we need from Action Kind&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;DECLARE @SubProcessKindId int, @Title nvarchar(100), @Duration int, @Desc nvarchar(400), @DepartmentId int&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;SELECT @Title = Title,&amp;nbsp;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt; &amp;nbsp; @Desc = [Description],&amp;nbsp;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt; &amp;nbsp; @DepartmentId = DepartmentId,&amp;nbsp;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt; &amp;nbsp; @Duration = Duration,&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt; &amp;nbsp; @SubProcessKindId = SubProcessKindId&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;FROM ActionKinds&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;WHERE (ID = @a_ActionKindId)&lt;/pre&gt;&lt;pre&gt;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;IF (@@ROWCOUNT = 0)&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;BEGIN&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;-- We checked the ActionKind Id is non-null so this means it is invalid.&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;RAISERROR('Invalid parameter. ActionKind %d does not exist', 11, 0, @a_ActionKindId) WITH SETERROR&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;RETURN&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;END&lt;br&gt;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;/***************************************************&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;					&lt;/span&gt;Do the useful work now&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt; ***************************************************/&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;-- Insert the action detail&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;INSERT INTO ActionDetails(Title, [Description])&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;VALUES (@Title, @Desc)&lt;/pre&gt;&lt;pre&gt;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;IF (@@ERROR &amp;lt;&amp;gt; 0)&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;BEGIN&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;			&lt;/span&gt;RAISERROR('FAILED to insert Action Detail.', 11, 0) WITH SETERROR&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;			&lt;/span&gt;RETURN&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;END&lt;/pre&gt;&lt;pre&gt;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;DECLARE @ActionDetailId int&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;SET @ActionDetailId = @@IDENTITY&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;-- Insert Action&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;INSERT INTO Actions WITH (ROWLOCK)&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;(&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;			&lt;/span&gt;ProcessId, PKAKId, ActionKindId, ActionDetailId,&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;			&lt;/span&gt;ActionState, ActualStartDate, Duration,&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;			&lt;/span&gt;ActualEndDate, AssignedDepartmentId, AssignedEmployeeId, SortOrder,&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;			&lt;/span&gt;-- We need to set these to a non-default value coz otherwise the&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;			&lt;/span&gt;-- UI takes a long time to reload the Gantt chart while the process&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;			&lt;/span&gt;-- is being edited.&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;			&lt;/span&gt;ExpectedStartDate, -- NORMAL_TIMESTAMP NOT NULL DEFAULT('1/1/1980'),&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;			&lt;/span&gt;ExpectedEndDate, -- NORMAL_TIMESTAMP NOT NULL DEFAULT('1/1/1980'),&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;			&lt;/span&gt;PlannedStartDate, -- NORMAL_TIMESTAMP NOT NULL DEFAULT('1/1/1980'),&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;			&lt;/span&gt;PlannedEndDate -- NORMAL_TIMESTAMP NOT NULL DEFAULT('1/1/1980'),&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;)&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;VALUES&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;(&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;			&lt;/span&gt;@a_ProcessId, NULL, @a_ActionKindId, @ActionDetailId,&amp;nbsp;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;			&lt;/span&gt;dbo.ActionState_Waiting(), NULL, @Duration,&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;			&lt;/span&gt;NULL, @DepartmentId, NULL, -1,&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;			&lt;/span&gt;GETDATE(), DATEADD(day, 1, GETDATE()),&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;			&lt;/span&gt;GETDATE(), DATEADD(day, 1, GETDATE())&amp;nbsp;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;)&lt;/pre&gt;&lt;pre&gt;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;IF (@@ERROR&amp;lt;&amp;gt;0)&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;BEGIN&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;			&lt;/span&gt;RAISERROR('Failed to insert Action', 11, 0) WITH SETERROR&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;			&lt;/span&gt;RETURN&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;END&lt;/pre&gt;&lt;pre&gt;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;DECLARE @ActionId int&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;SET @ActionId = @@IDENTITY&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;SET @a_NewActionId = @ActionId&lt;/pre&gt;&lt;pre&gt;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;-------------------------------&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;-- Insert Dynamic Fields&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;-------------------------------&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;DECLARE DynamicFieldsCursor CURSOR FAST_FORWARD FOR&amp;nbsp;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;SELECT ActionKindDynamicFields.DynamicFieldId,&amp;nbsp;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt; &amp;nbsp; DynamicFields.FieldType,&amp;nbsp;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt; &amp;nbsp; DynamicFields.DefaultValue&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;FROM Actions WITH (NOLOCK)&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;INNER JOIN Processes WITH (NOLOCK) ON (Actions.ProcessId = Processes.ID)&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;INNER JOIN ActionKinds WITH (NOLOCK) ON (Actions.ActionKindId = ActionKinds.ID)&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;INNER JOIN ActionKindDynamicFields WITH (NOLOCK) ON (ActionKinds.ID = ActionKindDynamicFields.ActionKindId)&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;INNER JOIN DynamicFields WITH (NOLOCK) ON (DynamicFields.ID = ActionKindDynamicFields.DynamicFieldId)&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;WHERE (Actions.ID = @ActionId)&lt;br&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;&lt;br&gt;	&lt;/span&gt;OPEN DynamicFieldsCursor&lt;br&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;&lt;br&gt;	&lt;/span&gt;DECLARE @DynamicFieldId int, @FieldType int, @DefaultFieldValue money&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;FETCH NEXT FROM DynamicFieldsCursor INTO @DynamicFieldId, @FieldType, @DefaultFieldValue&lt;br&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;&lt;br&gt;	&lt;/span&gt;WHILE (@@FETCH_STATUS = 0)&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;BEGIN&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;DECLARE @ValueToInsert nvarchar(80)&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;SET @ValueToInsert = NULL&lt;br&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;&lt;br&gt;		&lt;/span&gt;SELECT &amp;nbsp;@FieldType = FieldType&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;FROM DynamicFields WITH (NOLOCK)&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;WHERE ID = @DynamicFieldId&lt;br&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;&lt;br&gt;		&lt;/span&gt;IF (@DefaultFieldValue IS NOT NULL)&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;BEGIN&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;			&lt;/span&gt;IF (@FieldType IN (dbo.DynamicFieldType_Boolean(), dbo.DynamicFieldType_Enum(), dbo.DynamicFieldType_Integer()))&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;				&lt;/span&gt;SET @ValueToInsert = CONVERT(nvarchar, CONVERT(int, @DefaultFieldValue))&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;			&lt;/span&gt;ELSE IF (@FieldType = dbo.DynamicFieldType_Money())&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;				&lt;/span&gt;SET @ValueToInsert = CONVERT(nvarchar, @DefaultFieldValue)&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;END&lt;br&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;&lt;br&gt;		&lt;/span&gt;INSERT INTO DynamicFieldValues(DynamicFieldId, ActionId, FieldValue)&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;VALUES (@DynamicFieldId, @ActionId, @ValueToInsert)&lt;/pre&gt;&lt;pre&gt;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;			&lt;/span&gt;IF (@@ERROR&amp;lt;&amp;gt;0)&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;			&lt;/span&gt;BEGIN&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;				&lt;/span&gt;RAISERROR('Failed to insert into DynamicFieldValue table', 11, 0) WITH SETERROR&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;				&lt;/span&gt;RETURN&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;			&lt;/span&gt;END&lt;br&gt;&lt;/pre&gt;&lt;pre&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;&lt;br&gt;		&lt;/span&gt;FETCH NEXT FROM DynamicFieldsCursor INTO @DynamicFieldId, @FieldType, @DefaultFieldValue&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;END&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;br&gt;&lt;/span&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;CLOSE DynamicFieldsCursor&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;DEALLOCATE DynamicFieldsCursor&lt;/pre&gt;&lt;pre&gt;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;-- Action dependencies will be handled by the caller&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;-- Graph data will be handled by the caller&lt;/pre&gt;&lt;pre&gt;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;/********************&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;SubProcesses&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;*********************/&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;-- Is it a subprocess action?&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;IF (@SubProcessKindId IS NULL)&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;RETURN&lt;br&gt;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;DECLARE @SubProcessId int, @MasterProcessId int&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;SET @MasterProcessId = dbo.fn_ProcessGroupMaster(@a_ProcessId)&lt;/pre&gt;&lt;pre&gt;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;-- In the normal InsertProcess procedure, the date we pass is the&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;-- planned date of the action. Currently we don't have that so we&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;-- will pass the current timestamp. Dates recalculation will be&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;-- trigger by the caller when all changes are finished.&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;DECLARE @SubProcessStartDate NORMAL_TIMESTAMP&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;SET @SubProcessStartDate = GETDATE()&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;EXEC InsertNewProcess @MasterProcessId,&amp;nbsp;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;						&lt;/span&gt; &amp;nbsp;@SubProcessKindId,&amp;nbsp;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;						&lt;/span&gt; &amp;nbsp;@SubProcessStartDate,&amp;nbsp;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;						&lt;/span&gt; &amp;nbsp;@SubProcessId OUTPUT&lt;/pre&gt;&lt;pre&gt;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;IF (@@ERROR &amp;lt;&amp;gt; 0)&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;BEGIN&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;			&lt;/span&gt;RAISERROR('FAILED to insert Sub Process', 11, 0) WITH SETERROR&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;			&lt;/span&gt;RETURN&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;END&lt;/pre&gt;&lt;pre&gt;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;INSERT INTO ProcessRelations(MasterProcessId, ActionId, ParentProcessId, ChildProcessId)&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;	&lt;/span&gt;VALUES (@MasterProcessId, @ActionId, @a_ProcessId, @SubProcessId)&lt;/pre&gt;&lt;pre&gt;&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;IF (@@ERROR &amp;lt;&amp;gt; 0)&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;BEGIN&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;			&lt;/span&gt;RAISERROR('FAILED to insert into ProcessRelations', 11, 0) WITH SETERROR&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;			&lt;/span&gt;RETURN&lt;br&gt;&lt;span class="Apple-tab-span" style="white-space:pre;"&gt;		&lt;/span&gt;END&lt;br&gt;END&lt;/pre&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;/p&gt;</description></item><item><title>Απ: C# ή VB.NET ?</title><link>https://www.dotnetzone.gr:443/cs/forums/thread/71721.aspx</link><pubDate>Mon, 10 Dec 2012 20:42:20 GMT</pubDate><guid isPermaLink="false">2622095e-976c-431a-859e-16783ec7ecd7:71721</guid><dc:creator>BruteForce</dc:creator><slash:comments>0</slash:comments><comments>https://www.dotnetzone.gr:443/cs/forums/thread/71721.aspx</comments><wfw:commentRss>https://www.dotnetzone.gr:443/cs/forums/commentrss.aspx?SectionID=14&amp;PostID=71721</wfw:commentRss><description>&lt;BLOCKQUOTE&gt;&lt;div&gt;&lt;img src="http://www.dotnetzone.gr/cs/Themes/default/images/icon-quote.gif"&gt; &lt;strong&gt;Tsopi:&lt;/strong&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;"γιατί να παιδευτώ", ένα από τα χαρακτηριστικά που ξεχωρίζει τους junior από τους senior developers&lt;br&gt;&lt;br&gt;&lt;/b&gt;Όχι στην περίπτωση μου. Όταν έχω μια funtion που να εμφανίζει τον χρόνο που έχει περάσει από την αρχή του τραγουδιού σε ένα media player, "σκασίλα" μου αν κάποια στιγμή βγέι ένα οποιοδήποτε πρόβλημα και το φάει η On Error Resume Next. Δεν είναι όλα τα προβλήματα κρίσιμα και χρηματοοικονομικά...&lt;br&gt;&lt;/div&gt;&lt;/BLOCKQUOTE&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Λάθος. Μέγα λάθος. Είναι σαν να λέει κάποιος "Εγώ είμαι ευγενικός άνθρωπος, αλλά μόνο εκεί που χρειάζεται, δεν είναι το ίδιο σπουδαίοι όλοι οι άνθρωποι ώστε να απαιτείται ευγένεια".&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Το να "καταπίνει" ή να αγνοεί ο κώδικας το οποιοδήποτε λάθος, είναι... ΛΑΘΟΣ.&lt;/div&gt;&lt;div&gt;Είναι λάθος να αγνοείς το λάθος.&lt;/div&gt;&lt;div&gt;Κάνει και αναδρομικό λογοπαίγνιο.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Όποιος νομίζει ότι είναι σε θέση να κρίνει ποια λάθη έχουν σημασία και ποια όχι, είναι μάλλον πολύ νέος.&lt;/div&gt;&lt;div&gt;Κατ'αρχάς, όταν γράφεις κώδικα, ποτέ δεν ξέρεις πώς θα επεκταθεί η εφαρμογή, και από που θα καλείται ο κώδικάς σου μετά από μερικά χρόνια.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Ξέρεις ποιός είναι ένας από τους βασικούς λόγους που πέτυχαν τα Windows; Λόγος που διατρέχει τα τελευταία 15 χρόνια και βάλε;&lt;/div&gt;&lt;div&gt;H Blue Screen of Death.&lt;/div&gt;&lt;div&gt;Η BSOD δεν είναι crash, είναι ουσιαστικά ένα kernel message box, που σου λέει όσο πιο ευγενικά γίνεται ότι έκανες κάτι που δεν προβλέπεται, εξαιτίας κάποιου προγραμματιστικού λάθους φυσικά.&lt;/div&gt;&lt;div&gt;Το οποίο λάθος προφανώς ΔΕΝ μπορούσες να φανταστείς εκ των προτέρων, αλλιώς δεν θα το έκανες.&lt;/div&gt;&lt;div&gt;Οπότε ο kernel σου κάνει τη χάρη να μην κρασάρει αλλά να παγώσει το σύστημα ώστε να δεις τι έκανες λάθος.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Ο λόγος που ο kernel είναι τόσο ευγενικός;;;&lt;/div&gt;&lt;div&gt;Διότι αν το προγραμματιστικός σου λάθος αγνοηθεί, θα γίνει propagate και μετά όχι απλώς είναι βέβαιο ότι θα ακολουθήσουν νέα runtime errors, αλλά θα γίνει τέτοιος χαμός που κανείς δεν θα μπορεί να καταλάβει ποιό είναι το αρχικό λάθος.&lt;/div&gt;&lt;div&gt;Διότι το αρχικό λάθος είναι αυτό που φταίει συνήθως.&amp;nbsp;Όπως όταν παίρνουμε έναν καταρράκτη από compilation errors στη C++.&lt;/div&gt;&lt;div&gt;Και μπορεί στο development PC να έχεις την πολυτέλεια να κάνεις debug, όταν όμως σαλτάρει ένας production server/PC τότε την έχεις άσχημα.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;H λογική του να μην αγνοούμε τα λάθη είναι σοφή.&lt;/div&gt;&lt;div&gt;Όπως π.χ. το να μην αφήνεις warnings στο build, ή να αγνοείς τα false positives του lint (ή άλλων εργαλείων) και να μην τα κάνεις suppress με σχόλιο.&lt;/div&gt;&lt;div&gt;Μαζεύονται μετά τα άσχετα warnings και κρύβουν τα ουστιαστικά.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Αν θες να αγνοήσεις την σοφία 20ετίας και πλέον μια ολόκληρης βιομηχανίας και να ξανανακαλύψεις τον τροχό, κάντο και αγνόησε τα errors κατά την κρίση σου.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Θα μου πεις, άλλη η κρισιμότητα του kernel και άλλη ενός music player.&lt;/div&gt;&lt;div&gt;Συμφωνώ. Με τον kernel πρέπει να είσαι ιδιαιτέρως εξαιρετικά ευγενικός, εώς διπλωματικός.&lt;/div&gt;&lt;div&gt;Με ένα χρηματοοικονομικό, εξαιρετικά ευγενικός.&lt;/div&gt;&lt;div&gt;Με τον music player, απλά πολύ ευγενικός.&lt;/div&gt;&lt;div&gt;Το να αγνοείς τα λάθη είναι μέγιστη αγένεια ;-)&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Τα λάθη είναι σαν τις γυναίκες.&amp;nbsp;&lt;/div&gt;&lt;div&gt;Δεν τους αρέσει να τα αγνοούμε.&lt;/div&gt;&lt;div&gt;Αν το κάνουμε, they come back with a vengeance ;-)&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;</description></item><item><title>Απ: C# ή VB.NET ?</title><link>https://www.dotnetzone.gr:443/cs/forums/thread/71713.aspx</link><pubDate>Sun, 09 Dec 2012 04:04:24 GMT</pubDate><guid isPermaLink="false">2622095e-976c-431a-859e-16783ec7ecd7:71713</guid><dc:creator>xabikos</dc:creator><slash:comments>0</slash:comments><comments>https://www.dotnetzone.gr:443/cs/forums/thread/71713.aspx</comments><wfw:commentRss>https://www.dotnetzone.gr:443/cs/forums/commentrss.aspx?SectionID=14&amp;PostID=71713</wfw:commentRss><description>Πάρα πολύ ενδιαφέρουσες απαντήσεις για όλους όσους θέλουν να βελτιώνουν την ποιήτητα του κώδικα που γράφουν και να προσπαθούν να μαθαίνουν καινούρια πράγματα.&lt;br&gt;Προσωπικά όμως πάντα βρίσκω ποιο ενδιαφέρον όταν βλέπω και ένα συγκεκριμένο παράδειγμα αυτού που περιγράφεται με λόγια. &lt;br&gt;Ετσί για μένα θα ήταν πολύ ενδιαφέρον αν ο BruteForce μπορούσε να δώσει ένα παράδειγμα ειδικά για την τακτική στην C# να χρησιμοποιούμε static functions και τι πλεονεκτήματα έχει αυτό.&lt;br&gt;Δεν ξέρω βέβαια αν είναι εφικτό να δούμε κάτι τέτοιο με ένα μικρό παράδειγμα ή απαιτεί περισσότερη δουλειά.&lt;br&gt;</description></item><item><title>Απ: C# ή VB.NET ?</title><link>https://www.dotnetzone.gr:443/cs/forums/thread/71701.aspx</link><pubDate>Fri, 07 Dec 2012 05:56:09 GMT</pubDate><guid isPermaLink="false">2622095e-976c-431a-859e-16783ec7ecd7:71701</guid><dc:creator>Tsopi</dc:creator><slash:comments>0</slash:comments><comments>https://www.dotnetzone.gr:443/cs/forums/thread/71701.aspx</comments><wfw:commentRss>https://www.dotnetzone.gr:443/cs/forums/commentrss.aspx?SectionID=14&amp;PostID=71701</wfw:commentRss><description>&lt;b&gt;"γιατί να παιδευτώ", ένα από τα χαρακτηριστικά που ξεχωρίζει τους junior από τους senior developers&lt;br&gt;&lt;br&gt;&lt;/b&gt;Όχι στην περίπτωση μου. Όταν έχω μια funtion που να εμφανίζει τον χρόνο που έχει περάσει από την αρχή του τραγουδιού σε ένα media player, "σκασίλα" μου αν κάποια στιγμή βγέι ένα οποιοδήποτε πρόβλημα και το φάει η On Error Resume Next. Δεν είναι όλα τα προβλήματα κρίσιμα και χρηματοοικονομικά...&lt;br&gt;&lt;br&gt;Και δεν βαριέμαι να γράψω κώδικα για να πιάνω τα προβλήματα. Στο ίδιο player, η funtion που αποθήκευε ένα αρχείο για backup ρυθμίσεις, είχε 15 εντολές. Όχι μόνο είχα βάλει Try Catch σε ΚΑΘΕ εντολή , αλλά είχα φτιάξει και enum για να το κάνω return ώστε να ξέρω ακριβώς που υπήρξε πρόβλημα και ποιο ήταν ( προκειμένου να το αντιμετωπίσω κατάλληλα ).&lt;br&gt;&lt;br&gt;Και δυστυχώς δεν είμαι junior developer... επειδή δεν έχω βρεi δουλειά ακόμα :P&lt;br&gt;</description></item><item><title>Απ: C# ή VB.NET ?</title><link>https://www.dotnetzone.gr:443/cs/forums/thread/71698.aspx</link><pubDate>Thu, 06 Dec 2012 23:08:28 GMT</pubDate><guid isPermaLink="false">2622095e-976c-431a-859e-16783ec7ecd7:71698</guid><dc:creator>BruteForce</dc:creator><slash:comments>0</slash:comments><comments>https://www.dotnetzone.gr:443/cs/forums/thread/71698.aspx</comments><wfw:commentRss>https://www.dotnetzone.gr:443/cs/forums/commentrss.aspx?SectionID=14&amp;PostID=71698</wfw:commentRss><description>Good points.&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Για να κάνω μια αναλογία που νομίζω είναι σχετική, ο απόλυτος παράδεισος του error handling είναι οι Database Servers.&lt;/div&gt;&lt;div&gt;BEGIN TRANSACTION και αν κάτι πάει στραβά ROLLBACK και όλα καθαρά και ανέγγιχτα.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Αυτό είναι τόσο βοηθητικό, που το Win32 και ο kernel των Windows, έχουν ρουτίνες για transactional handling της registry και το file system!&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Στον κώδικα C++/C# όμως το handling είναι σαφώς πιο δύσκολο, γιατί ο κώδικας πρέπει να γραφτεί functional.&lt;/div&gt;&lt;div&gt;Συνήθως μετά από ένα error το object είναι "άχρηστο", δηλαδή δεν έχουμε ιδέα σε τι κατάσταση βρίσκεται εσωτερικά. Άρα πρέπει να το σουτάρουμε άμεσα.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Τώρα πρέπει να καταλάβουμε κάτι βασικό.&lt;/div&gt;&lt;div&gt;Κάθε member function που &lt;u&gt;γράφει&lt;/u&gt; σε member variable, είναι σχεδόν σαν να γράφει σε global μεταβλητή.&lt;/div&gt;&lt;div&gt;Όσο πιο πολλές functions γράφουν, τόσο περισσότερο χάνεται η μπάλα.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Μια τεχνική που χρησιμοποιώ εγώ στη C#, είναι να κάνω όσο πιο πολλές member functions static, οπότε δεν μπορούν να αγγίξουν τίποτα, μόνο να δεχτούν παραμέτρους και να επιστρέψουν output. Έτσι ελαχιστοποιείται ο κώδικας που είναι σε θέση να πειράξει μια member variable.&lt;/div&gt;&lt;div&gt;Επίσης ελαχιστοποιούνται τα references στις member variables και όταν κάνω Find References δεν μου βγαίνει ένας καταρράκτης, άρα μπορώ να βγάλω άκρη εύκολα και να ελέγξω τι γίνεται σε κάθε σημείο που χρησιμοποιείται η member variable.&lt;/div&gt;&lt;div&gt;Αυτό βέβαια σημαίνει ότι μπορεί να γράψουμε λίγο κώδικα παραπάνω, γιατί διάφορες member variables θα πρέπει να τις περνάμε παραμέτρους στις static functions, αντί αυτές να τις διαβάζουν.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Η C++ είχε το εκπληκτικό feature των const member functions.&lt;/div&gt;&lt;div&gt;Αυτές μπορούν να διαβάσουν το state του object, αλλά ΔΕΝ μπορούν να το αλλάξουν.&lt;/div&gt;&lt;div&gt;Απλά ΤΕΛΕΙΟ. Και επιπλέον έχει const parameters, οπότε περνάω ένα object σαν const reference και ο κώδικας ΔΕΝ μπορεί να το πειράξει, αλλά μόνο να το διαβάσει.&lt;/div&gt;&lt;div&gt;Το 80% των C++ member functions στον κώδικά μου είναι const και έχω το κεφάλι μου ήσυχο.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;Δυστυχώς, επειδή η C# είναι φτιαγμένη ώστε να είναι κατανοητή και από πιθήκους, δεν έχει constness σε παραμέτρους και member functions.&lt;/div&gt;&lt;div&gt;Οπότε ακόμα και σε static member function, αν περάσω ένα member variable που είναι List&amp;lt;&amp;gt; για παράμετρο, η static function μπορεί να του βγάλει τα μάτια.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Με κάτι τέτοια η C++&amp;nbsp;προστατεύει&amp;nbsp;τους σοβαρούς προγραμματιστές, και μου επιτρέπει να γράψω κώδικα που κάποιος στο παρελθόν τον χαρακτήρισε "Φρούριο".&lt;/div&gt;&lt;div&gt;Και κόβοντας κάτι τέτοια η C# προστατεύει τους κακούς προγραμματιστές και τους &lt;u&gt;διευκολύνει&lt;/u&gt; να μπαίνουν στο επάγγελμα και να κάνουν τη ζωή ολονών μας πολύ πιο δύσκολη, καθώς αντί για φρούρια φτιάχνουν κακοσχεδιασμένες στάνες.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Φανταστείτε απλά, κάθε 3-4 χρόνια να απλοποιούσαν την Ιατρική όπως "απλοποιούν" το development, ώστε να έχουμε περισσότερους γιατρούς, γιατί αυτοί που έχουμε δεν φτάνουν.&lt;/div&gt;&lt;div&gt;Δεν θα ήταν τραγικό;;;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;</description></item><item><title>Απ: C# ή VB.NET ?</title><link>https://www.dotnetzone.gr:443/cs/forums/thread/71697.aspx</link><pubDate>Thu, 06 Dec 2012 22:20:21 GMT</pubDate><guid isPermaLink="false">2622095e-976c-431a-859e-16783ec7ecd7:71697</guid><dc:creator>Παναγιώτης Καναβός</dc:creator><slash:comments>0</slash:comments><comments>https://www.dotnetzone.gr:443/cs/forums/thread/71697.aspx</comments><wfw:commentRss>https://www.dotnetzone.gr:443/cs/forums/commentrss.aspx?SectionID=14&amp;PostID=71697</wfw:commentRss><description>&lt;BLOCKQUOTE&gt;&lt;div&gt;&lt;img src="http://www.dotnetzone.gr/cs/Themes/default/images/icon-quote.gif"&gt; &lt;strong&gt;BruteForce:&lt;/strong&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;div&gt;H σωστή κατανόηση και σωστή χρήση των exceptions είναι κάτι που ΔΕΝ μπορεί να συλλάβει ο μέσος προγραμματιστής.&lt;/div&gt;&lt;div&gt;Απλά ΔΕΝ ΜΠΟΡΕΙ.&lt;/div&gt;&lt;/div&gt;&lt;/BLOCKQUOTE&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Και όχι μόνο ο μέσος προγραμματιστής. Το HttpWebRequest θα πετάξει exception είτε ζητήσεις μία σελίδα που λείπει και πάρεις 404, είτε ζητήσεις μόνο την αλλαγμένη σελίδα και πάρεις 304. Το οποίο σημαίνει ότι η σελίδα δεν άλλαξε, και δεν πρόκειται ούτε καν για error. Ευτυχώς συνήλθαν λίγο στο .ΝΕΤ 4.5 και ο HttpClient δεν σηκώνει αυτόματα exceptions ...&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Θα πάω λίγο παραπέρα. Τα exception είναι απλά ένας μηχανισμός για να σε &lt;i&gt;&lt;b&gt;ειδοποιήσει&lt;/b&gt; και να σε &lt;b&gt;προστατεύσει&amp;nbsp;&lt;/b&gt;&lt;/i&gt;&amp;nbsp;σε exceptional καταστάσεις. ΔΕΝ είναι μηχανισμός ο οποίος θα χειριστεί τις καταστάσεις. Αυτό είναι δουλειά του προγραμματιστή, να σχεδιάσει τον κώδικα του έτσι ώστε να αντιμετωπίσει σωστά αυτή την έκτακτη κατάσταση. Το παράδειγμα με το ρελαί δεν είναι τυχαίο.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Είναι άλλο θέμα πως θα φτιάξεις κώδικα που &lt;b&gt;αντέχει&lt;/b&gt;&amp;nbsp;σε exceptions και άλλο πως θα &lt;b&gt;χειριστείς &lt;/b&gt;τα exceptions ή τα errors:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Αντοχή (ή exception safety) σημαίνει ότι ο κώδικας θα δουλέψει σωστά ακόμα και αν υπάρξει exception και δεν θα αφήσει πράγματα στον αέρα. Κώδικας ο οποίος πειράζει global/external variables για παράδειγμα, δεν αντέχει τόσο όσο κώδικας ο οποίος τροποποιεί μόνο local coipes των variables και τα επιστρέφει. Ένα function που κάνει 5 διαφορετικά πράγματα είναι πιο επικίνδυνο από 5 μικρότερα functions που κάνουν μόνο 1 πράγμα. Τα functions του C++ Standard Template Library είναι exception safe παρότι (ή επειδή) δεν χρησιμοποιούν try/catch ακριβώς επειδή έχουν σχεδιαστεί ώστε να είναι exception safe.&lt;/li&gt;&lt;li&gt;Ο χειρισμός έχει να κάνει με το τί κάνεις αφού "πέσει" η ασφάλεια και συνεφέρεις το πρόγραμμα. Τί κάνεις, συνεχίζεις με το επόμενο βήμα, ακολουθείς άλλη διαδρομή, δοκιμάζεις ξανά? Στην πραγματικότητα μιλάμε για κώδικα ο οποίος δεν έχει σχέση (ή θέση) με το catch, αλλά εκτελείται αφού ολοκληρωθεί το catch και διαπιστωθεί ότι η εκτέλεση θα πρέπει να συνεχίσει με διαφορετικό τρόπο.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Και τα δύο θέματα απαιτούν προσεκτική σχεδίαση τόσο σε επίπεδο μεμονωμένων function όσο και στη σχεδίαση ολόκληρων modules.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Για να πάμε πίσω στο παράδειγμα του ρελαί, δεν αρκεί να πέσει το ρελαί για να σε προστατεύσει από ηλεκτροπληξία ή πυρκαγιά. Πρέπει η κάθε ηλεκτρική συσκευή και τα καλώδια να έχουν σχεδιαστεί έτσι ώστε να αντέξουν στην απότομη διακοπή ή επαναφορά του ρεύματος.&amp;nbsp;&lt;/div&gt;</description></item><item><title>Απ: C# ή VB.NET ?</title><link>https://www.dotnetzone.gr:443/cs/forums/thread/71696.aspx</link><pubDate>Thu, 06 Dec 2012 21:31:10 GMT</pubDate><guid isPermaLink="false">2622095e-976c-431a-859e-16783ec7ecd7:71696</guid><dc:creator>Παναγιώτης Καναβός</dc:creator><slash:comments>0</slash:comments><comments>https://www.dotnetzone.gr:443/cs/forums/thread/71696.aspx</comments><wfw:commentRss>https://www.dotnetzone.gr:443/cs/forums/commentrss.aspx?SectionID=14&amp;PostID=71696</wfw:commentRss><description>&lt;BLOCKQUOTE&gt;&lt;div&gt;&lt;img src="http://www.dotnetzone.gr/cs/Themes/default/images/icon-quote.gif"&gt; &lt;strong&gt;BruteForce:&lt;/strong&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;div&gt;Τα συγκεκριμένα components εμπλεκόντουσαν στην αποστολή εντολών στο ΧΑΑ... μάλλον δεν υπερέβαλα. Και τόσα χρόνια ΠΟΤΕ δεν υπήρξε bug στις live εγκαταστάσεις.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Συνεπώς: ΔΕΝ φταίει η γλώσσα. ΦΤΑΕΙ ο προγραμματιστής.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;/BLOCKQUOTE&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Και μόλις θυμήθηκα την&lt;a href="http://en.wikipedia.org/wiki/Knight_Capital_Group"&gt; Knight Capital&lt;/a&gt; και το algorithmic trading. Όπως ίσως έχει διαπιστώσει και ο Δημήτρης, ο κόσμος που ασχολείται με χρηματιστηριακά δεν παίρνει και τόσο στα σοβαρά την ποιότητα και το error/exception safety του κώδικα. Υπάρχει ΠΟΛΥ On Error Resume Next ή catch {} στα χρηματιστηριακά. Σου λέει ο άλλος, αν βγει 1 μέρα νωρίτερα, θα βγάλω 1Μ παραπάνω και αν έχει προβληματάκια, ε θα τα συγυρίσω. Άσχετα αν η κακή σχεδίαση παράγει 1Μ προβλήματα.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Και βγαίνει που λες η Knight Capital με νέα υπηρεσία για algorithmic trading τον Αύγουστο, διαφημίζοντας ότι ήταν η πρώτη που έβγαλε υποστήριξη για τη ΧΨΩ πλατφόρμα, πρίν απ' όλους και τα σχετικά, πρώτη στην αγορά και όλα αυτά. Προφανώς, με το να βγούνε πρώτοι θα κερδίζανε κάμποσα μύρια σε σχέση με όσους θα έρχονταν μετά, οπότε κόψανε πάλι δρόμο. Αποτέλεσμα ....&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://www.telegraph.co.uk/finance/markets/9448893/Knight-Capital-has-48-hours-to-save-itself-after-IT-glitch-causes-440m-loss.html"&gt;Μέσα σε 45 λεπτά χάσανε $450 εκατομμύρια&lt;/a&gt;&amp;nbsp;. Κάποιος "&lt;a href="http://www.telegraph.co.uk/finance/newsbysector/banksandfinance/9475292/Knight-Capitals-440m-trading-loss-caused-by-disused-software.html"&gt;ξεχασμένος&lt;/a&gt;" κώδικας έσκασε,&amp;nbsp;και οι εφαρμογές πουλούσανε φθηνά και αγοράζανε ακριβά λόγω λαθών. Η&amp;nbsp;εταιρεία αναγκάστηκε να πουληθεί ουσιαστικά μέσα σε 4 μέρες.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Πάω στοίχημα πάντως ότι ο κώδικας ήταν γραμμένος σε C και έτρεχε σε Linux για να είναι πιο γρήγορος. Όπως όμως λέει και ο Δημήτρης, είναι ο προγραμματιστής, όχι η γλώσσα που μετράει.&lt;/div&gt;</description></item><item><title>Απ: C# ή VB.NET ?</title><link>https://www.dotnetzone.gr:443/cs/forums/thread/71695.aspx</link><pubDate>Thu, 06 Dec 2012 21:03:53 GMT</pubDate><guid isPermaLink="false">2622095e-976c-431a-859e-16783ec7ecd7:71695</guid><dc:creator>BruteForce</dc:creator><slash:comments>0</slash:comments><comments>https://www.dotnetzone.gr:443/cs/forums/thread/71695.aspx</comments><wfw:commentRss>https://www.dotnetzone.gr:443/cs/forums/commentrss.aspx?SectionID=14&amp;PostID=71695</wfw:commentRss><description>Συμφωνώ με τα τελευταία σχόλια του Παναγιώτη.&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Πριν από 2 χρόνια είχα μια μακροσκελή κουβέντα με τον Σπινέλλη.&lt;/div&gt;&lt;div&gt;Το θέμα μας ήταν το "ποιό ποσοστό του κώδικα είναι error handling" και αν μπορούν να φτιαχτούν εργαλεία metrics που να το μετράνε.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Η δική του εκτίμηση ήταν ότι για business critical συστήματα ήταν minimum 30%, και για mission critical (λειτουργικά, κλπ) περίπου 40%.&lt;/div&gt;&lt;div&gt;Εγώ, με βάση την προσωπική μου εμπειρία, δηλαδή το πώς γράφω κώδικα και πώς βάζω άλλους να γράφουν κώδικα, ήταν 40% και 50% αντίστοιχα.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Είναι εξαιρετικά εύκολο να γράψει κάποιος κώδικα που να δουλεύει σωστά, όταν ΟΛΑ πάνε καλά.&lt;/div&gt;&lt;div&gt;Το αγγούρι είναι όταν τα πράγματα δεν πάνε καλά, και υπάρχουν χιλιάδες τρόποι να μην πάνε καλά.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Για εμάς που είμαστε "παλιοί" τα exceptions είναι πολύ απλή ιστορία.&lt;/div&gt;&lt;div&gt;Υπήρχαν από την εποχή του DOS και Windows 3.1. Απλά setjmp και longjump και ήσουν έτοιμος.&lt;/div&gt;&lt;div&gt;Η χρήση τους υπερσπάνια, όπως π.χ. σε compiler γραμμένο σε C++ που κάνει την λεγόμενη αναδρομική κατάβαση.&lt;/div&gt;&lt;div&gt;Βέβαια δεν έκανε stack cleanup, έπρεπε να φροντίζω να κρατάω όλα τα objects κάπου, ώστε αν σκάσει "exception" να κάνω cleanup μόνος μου.&lt;/div&gt;&lt;div&gt;Σιγά το πράγμα.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Μετά τα NT βάλανε το SEH (Structured Exception Handling), το οποίο ήταν σε επίπεδο λειτουργικού, μαζί με finally, και δούλευε και σε kernel mode.&lt;/div&gt;&lt;div&gt;Πάλι, δεν είχε object cleanup. No big deal. Υπάρχουν εκατομμύρια γραμμες κώδικα που δεν χρησιμοποιούν objects.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Μετά τα έβαλε και η C++, ώστε κατά το stack unwinding να γίνονται cleanup τα local object (on stack), και μάλιστα επιτρέπει και μίξη SEH και C++ exceptions.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Πρέπει να καταλάβετε κάτι βασικό. Τα exceptions δεν είναι τίποτα άλλο παρά ένα φρικαλέο GOTO, που διατρέχει δεκάδες functions, με πιθανώς άγνωστο προορισμό, μαζί με ολίγη από gabrage colletion.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Είχε γράψει ο Joel Spolsky παλιότερα πάνω στο θέμα, ότι μισεί τα exceptions, και κάπου είχα γράψει και εγώ ότι τα υπερμισώ.&lt;/div&gt;&lt;div&gt;Ο λόγος;;;;&lt;/div&gt;&lt;div&gt;H σωστή κατανόηση και σωστή χρήση των exceptions είναι κάτι που ΔΕΝ μπορεί να συλλάβει ο μέσος προγραμματιστής.&lt;/div&gt;&lt;div&gt;Απλά ΔΕΝ ΜΠΟΡΕΙ.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Εγώ ξέρω πώς να τα χρησιμοποιήσω σωστά, αλλά γράφω κώδικα 20 χρόνια, έχω γράψει άπειρο mission critical kernel mode κώδικα, άπειρο business critical C++ κώδικα και κατανοώ πώς πρέπει να χειριζόμαστε τα exceptions και τι πάει να πει error handling.&lt;/div&gt;&lt;div&gt;Ο μέσος προγραμματιστής απλά θα κάνει ένα exception spaghetti.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Για να το θέσω απλά: Exception και Error ΔΕΝ ΕΙΝΑΙ ΤΟ ΙΔΙΟ ΠΡΑΓΜΑ.&lt;/div&gt;&lt;div&gt;Το .ΝΕΤ και η Java δυστυχώς το έχουν κάνει να φαίνεται το ίδιο, αλλά ΔΕΝ είναι το ίδιο.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Exceptions are exceptional or unpredictable problems, errors are expected or predictable problems.&lt;/div&gt;&lt;div&gt;Τα errors μπορούν να γίνουν handle, τα πραγματικά exceptions σπανίως.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Δοκιμάζει το πρόγραμμά σου να κάνει login κάπου (μέχρι χτες έπαιζε) και σήμερα παίρνει authentication error.&lt;/div&gt;&lt;div&gt;Αυτό είναι error, δεν είναι exception. Εϊναι 100% προβλέψιμο ότι θα συμβεί.&lt;/div&gt;&lt;div&gt;Πας να διαβάσεις ένα αρχείο, και δεν έχεις access στο directory.&lt;/div&gt;&lt;div&gt;Ή φορτώνεις και πας να σώσεις ένα αρχείο και κάποιος το έχει κάνει read-only. Έχεις security permissions, αλλά το αρχείο είναι read-only.&lt;/div&gt;&lt;div&gt;Γράφεις/Διαβάζεις από ένα USB Stick και ο χρήστης το βγάζει και την κάνει για Μπαχάμες.&lt;/div&gt;&lt;div&gt;Είναι errors όχι exceptions. 100% προβλέψιμο ότι θα συμβούν.&lt;/div&gt;&lt;div&gt;Κάνει ο κώδικάς σου download και κόβεται το connection. Error, not exception.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Τι είναι exceptions;&lt;/div&gt;&lt;div&gt;Κάνεις ένα query στη βάση, άμεσα ή έμμεσα, και τρώει timeout ή connection lost (στο local lan).&lt;/div&gt;&lt;div&gt;Κάνεις insert/update στη βάση και τρώει ο DB Server ένα disk full.&lt;/div&gt;&lt;div&gt;Πας να επικοινωνήσεις με έναν host και τρώς host unreachable.&lt;/div&gt;&lt;div&gt;Τρώς memory access violation exception (SEH) γιατί πήγες στα χωράφια.&lt;/div&gt;&lt;div&gt;Τρως memory allocation failure (memory full). To handling του memory full έχει πολύ πλάκα, έχω ρίξει απίστευτα γέλια. Τι μπορεί να κάνει ένα πρόγραμμα όταν τα Windows δεν μπορούν ούτε καν να ανοίξουν ένα message box???&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Δηλαδή, νομίζετε ότι&amp;nbsp;τόσα χρόνια&amp;nbsp;τα λειτουργικά (στο μεγαλύτερο ποσοστό του κώδικα) και οι device drivers γράφονται σε C μόνο και μόνο για το performance;;; Και η C++ έχει ουσιαστικά το ίδιο performance.&amp;nbsp;&lt;/div&gt;&lt;div&gt;Στα Windows όμως απαγορεύεται η C++ σε kernel code. ΑΠΑΓΟΡΕΥΕΤΑΙ. Γιατί;&lt;/div&gt;&lt;div&gt;(1) Έναν σοβαρό προγραμματιστή η C τον εξαναγκάζει να κάνει σωστό και πλήρες error handling. Και τον διευκολύνει να γράψει κώδικα δομημένο σωστά, γιατί ΔΕΝ του δίνει την επικύνδινη δυνατότητα για φρικαλέα GOTO.&lt;/div&gt;&lt;div&gt;(2) Ο κώδικας C είναι 100% predictable ώς προς το control flow. Δεν υπάρχουν "κρυμμένοι" copy constructors, destructors, garbage collections, overloaded operators κλπ που κάνουν μαγικά πράγματα χωρίς να παίρνει χαμπάρι τίποτα ο προγραμματιστής. Το control flow στη C είναι 100% explicit.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Ας το αφήσουμε αυτό, όποιος διαφωνεί δικαιωμά του.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Στην επιλογή γλώσσας προγραμματισμού και πλατφόρμας επικρατεί &lt;b&gt;&lt;u&gt;φυσική επιλογή&lt;/u&gt;&lt;/b&gt;.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Όσοι γουστάρουν και κατανοούν τα πολύπλοκα, κατανοούν και γουστάρουν το error handling και το επαγγελματικό error reporting, είναι οργανομένοι και τακτοποιημένοι μέσα στο μυαλό τους, δεν έχουν κανένα πρόβλημα με τη C και τη C++. Και καταλήγουν στις αντίστοιχες δουλειές.&lt;/div&gt;&lt;div&gt;Κάποιος που είναι ΟΚ με τη C++, την VB.NET θα την βρει μάλλον αηδιαστική, γιατί είναι πολύ πιο χύμα.&lt;/div&gt;&lt;div&gt;Εγώ την βρίσκω αηδιαστική και για τον επιπλέον λόγο, ότι εξαιτίας της φυσικής επιλογής, αν κουμάνταρα ένα project VB.NET τότε ΜΑΛΛΟΝ θα περιστοιχιζόμουνα από μέτριους και χύμα προγραμματιστές. Απλό είναι. Εγώ αν έγραφα VB.NET ο κώδικάς μου θα ήταν ολόιδιος με τη C#. 40-50% error handling. Και θα δούλευαν όλα ρολόι. Αλλά οι άλλοι γύρω μου;;;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Συνεπώς, αν δεχτείτε την εκτίμηση του Σπινέλλη για 30-40% error handling code, και κοιτάξετε ξανά τον κώδικά σας, θα δείτε ότι ΔΕΝ φταίει η γλώσσα. Οι προγραμματιστές φταίνε. Αυτά τα παραμύθια ότι η γλώσσα σε προστατεύει από λάθη, είναι &lt;b&gt;90% ΜΥΘΟΣ&lt;/b&gt;.&lt;/div&gt;&lt;div&gt;Δηλαδή το πρόβλημα της C/C++ είναι ότι δεν σε προστατεύει επειδή δεν ελέγχει τα array bounds???&lt;/div&gt;&lt;div&gt;Εϊπαμε, &lt;u&gt;φυσική επιλογή&lt;/u&gt;. Αν δεν μπορείς να γράψεις κώδικα που χειρίζεται ένα φουκαριάρικο array σωστά, τότε ΠΑΡΑΤΑ τη&amp;nbsp;C/C++, δεν είναι για σένα, δεν κάνεις για τις δουλειές που απαιτούν C++.&lt;/div&gt;&lt;div&gt;Ή το πρόβλημα είναι τα memory leaks?&amp;nbsp;Αν δεν μπορείς να χειριστείς σωστά το memory handling, τότε ΠΑΡΑΤΑ τη&amp;nbsp;C/C++, δεν κάνεις για τις δουλειές που απαιτούν C++.&lt;/div&gt;&lt;div&gt;Και το .ΝΕΤ έχει memory leaks, κάντε ένα google search να ρίξετε γέλιο. Ο Μανωλιός έβαλε τη σκούφια του αλλιώς.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Έχω γράψει και εγώ μπόλικη VB6 στο παρελθόν. Και είχα γράψει και OnError Resume Next. Αλλά έλεγχα το τιμημένο error code, δεν συνέχιζα στην καλή χαρά.&lt;/div&gt;&lt;div&gt;Αν έβλεπες την VB6 που έγραφα θα φρικάριζες, ήταν τόσοι πολλοί οι έλεγχοι που γινόντουσαν και τόσο αναλυτικό το error reporting που όντως ήταν το 30-40% του κώδικα. Και όταν τον βλέπανε οι normal VB6 programmers της εταιρίας, πραγματικά "φρικάρανε" και με ρωτάγανε αν πράγματι χρειάζεται τόσο πολύ error checking και μήπως υπερβάλλω.&lt;/div&gt;&lt;div&gt;Τα συγκεκριμένα components εμπλεκόντουσαν στην αποστολή εντολών στο ΧΑΑ... μάλλον δεν υπερέβαλα. Και τόσα χρόνια ΠΟΤΕ δεν υπήρξε bug στις live εγκαταστάσεις.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Συνεπώς: ΔΕΝ φταίει η γλώσσα. ΦΤΑΕΙ ο προγραμματιστής.&lt;/div&gt;&lt;div&gt;End of Story&lt;/div&gt;</description></item><item><title>Απ: C# ή VB.NET ?</title><link>https://www.dotnetzone.gr:443/cs/forums/thread/71692.aspx</link><pubDate>Thu, 06 Dec 2012 19:40:46 GMT</pubDate><guid isPermaLink="false">2622095e-976c-431a-859e-16783ec7ecd7:71692</guid><dc:creator>Παναγιώτης Καναβός</dc:creator><slash:comments>0</slash:comments><comments>https://www.dotnetzone.gr:443/cs/forums/thread/71692.aspx</comments><wfw:commentRss>https://www.dotnetzone.gr:443/cs/forums/commentrss.aspx?SectionID=14&amp;PostID=71692</wfw:commentRss><description>Ούτε προφήτης να ήτανε ... Ο Διομήδης Σπινέλλης ανέβασε &lt;a href="http://www.spinellis.gr/blog/20121205/"&gt;blog post&lt;/a&gt; για το πόσο επηρεάζονται διάφορες γλώσσες από τυχαία λάθη και κυρίως, κατά πόσο λανθασμένος κώδικας μπορεί να οδηγήσει σε λανθασμένα αποτελέσματα.&amp;nbsp;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Η VB δεν περιλαμβάνεται στις 10 γλώσσες λόγω ... περιορισμένης δραστηριότητας (στο IOBE, το Craigslist και σε διαθέσιμο source) αλλά η παρομοίως ανεκτική PHP βγήκε η χειρότερη γλώσσα, με ~35% των λαθών να οδηγούν σε λανθασμένα αποτελέσματα αντί για κάποιο σκάσιμο.&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Αντιθέτως, οι ασφαλέστερες ήταν οι γλώσσες της οικογένειας C/C++/C#/Java, με τη σειρά ... C++, C#, C και Java με ποσοστά 9-10%.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Η πλήρης δημοσίευση με νούμερα είναι &lt;a href="http://www.spinellis.gr/pubs/conf/2012-PLATEAU-Fuzzer/pub/html/fuzzer.html"&gt;εδώ&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;</description></item><item><title>Απ: C# ή VB.NET ?</title><link>https://www.dotnetzone.gr:443/cs/forums/thread/71689.aspx</link><pubDate>Thu, 06 Dec 2012 18:58:17 GMT</pubDate><guid isPermaLink="false">2622095e-976c-431a-859e-16783ec7ecd7:71689</guid><dc:creator>Παναγιώτης Καναβός</dc:creator><slash:comments>0</slash:comments><comments>https://www.dotnetzone.gr:443/cs/forums/thread/71689.aspx</comments><wfw:commentRss>https://www.dotnetzone.gr:443/cs/forums/commentrss.aspx?SectionID=14&amp;PostID=71689</wfw:commentRss><description>To On Error Resume Next είναι αντίστοιχο με το να τυλίγεις μία καμένη ασφάλεια με αλουμινόχαρτο - ή να κολλάς επάνω το ρελαί που έπεσε. Τα exceptions είναι ο τρόπος της γλώσσας και του λειτουργικού να σου πουν ότι κάτι σοβαρό συνέβει και κάηκε η ασφάλεια.&amp;nbsp;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Αν σκάει exception, θέλεις να δεις τί ήταν και να το διορθώσεις, όχι να το αγνοήσεις. Αν σε ενοχλεί το ότι η εκτέλεση μετακινείται μακρυά από το σημείο που έσκασε το exception, σημαίνει ότι το ίδιο το function σου έχει πρόβλημα και είναι πολύ μεγάλο. Αν ένα function κάνει τόσες δουλειές που σε απασχολεί πού θα συνεχίσει ο κώδικας μετά από ένα σφάλμα, πρέπει να το σπάσεις αμέσως.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Όσο για το "γιατί να παιδευτώ", ένα από τα χαρακτηριστικά που ξεχωρίζει τους junior από τους senior developers ή αυτούς που ασχολούνται επαγγελματικά με τον προγραμματισμό από αυτούς που προγραμματίζουν περιστασιακά, είναι ακριβώς ο βαθμός στον οποίο θέλουν να κάνουν κάτι σωστά αντί να το κάνουν "στα γρήγορα".&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Τέτοια "στα γρήγορα" έχουν οδηγήσει σε τραγελαφικές καταστάσεις, όπως τη φορά που βρήκα κώδικα που γύριζε υπόλοιπο λογαριασμού 0 σε χρηματιστηριακή γιατί ... μάντεψε .... κάποιος είχε βάλει On Error Resume Next στο σημείο που διάβαζε την ισοτιμία του δολλαρίου. Ένα σκασιματάκι που έκανε Resume Next και όλοι οι λογαριασμοί βγήκαν 0.&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;</description></item><item><title>Απ: C# ή VB.NET ?</title><link>https://www.dotnetzone.gr:443/cs/forums/thread/71678.aspx</link><pubDate>Thu, 06 Dec 2012 00:45:23 GMT</pubDate><guid isPermaLink="false">2622095e-976c-431a-859e-16783ec7ecd7:71678</guid><dc:creator>Tsopi</dc:creator><slash:comments>0</slash:comments><comments>https://www.dotnetzone.gr:443/cs/forums/thread/71678.aspx</comments><wfw:commentRss>https://www.dotnetzone.gr:443/cs/forums/commentrss.aspx?SectionID=14&amp;PostID=71678</wfw:commentRss><description>Παρόλο που συμφωνώ μαζί σου για την On Error GoTo/Resume Next, πραγματικά πιστεύω πως έχει λάβει υπερβολικά αρνητική κριτική και δεν της αξίζει γιατί υπάρχουν κάποιες περιπτώσεις που έχεις μια function που κάνει κάποια δουλειά κατά την οποία αν προκύψει πρόβλημα , να θες να το αγνοήσεις.&lt;br&gt;&lt;br&gt;&amp;nbsp;Γιατί πρέπει οπωσδήποτε δηλαδή να βάλεις Try Catch σε ένα μεγάλο μέρος της συνάρτησης και να μην βάλεις ένα On Error Resume Next στην αρχή ( αυξάνοντας και την ευκολία στο debuging γιατί όποτε δεν το θές , κάνεις comment out την πρώτη γραμμή και δεν ψάχνεις να βρεις το Try, to Catch και το End Try).&lt;br&gt;&lt;br&gt;Επίσης, ακόμα και μια λούπα μπορεί να θες να την αγνοήσεις. Θυμάμαι που έφτιαχνα ένα μικρό σύστημα εντοπισμού τραγουδιών μέσα στα αρχεία ενός User και καθώς έψαχνα τους φακέλους, κάποιοι δεν έδιναν στην εφαρμογή το δικαίωμα να τους ψάξει, και σταματούσε την εφαρμογή για έναν τελείως ασήμαντο λόγο. Οκ, δεν με νοιάζει να ψάξω φακέλους του συστήματος για να βρώ τα τραγούδια μου! Γιατί να παιδευτώ και να βάλω try?&lt;br&gt;&lt;br&gt;&lt;pre&gt;&lt;span style="color:Black;background-color:Transparent;font-family:Courier New;font-size:11px;font-weight:normal;"&gt;&lt;span style="color:Green;background-color:Transparent;font-family:Courier New;font-size:11px;font-weight:normal;"&gt;'QUICK SEARCH&lt;/span&gt;
            &lt;span style="color:Blue;background-color:Transparent;font-family:Courier New;font-size:11px;font-weight:normal;"&gt;If&lt;/span&gt; My.Settings.QuickSearch &lt;span style="color:Blue;background-color:Transparent;font-family:Courier New;font-size:11px;font-weight:normal;"&gt;Then&lt;/span&gt;
                &lt;span style="color:Blue;background-color:Transparent;font-family:Courier New;font-size:11px;font-weight:normal;"&gt;On&lt;/span&gt; &lt;span style="color:Blue;background-color:Transparent;font-family:Courier New;font-size:11px;font-weight:normal;"&gt;Error&lt;/span&gt; &lt;span style="color:Blue;background-color:Transparent;font-family:Courier New;font-size:11px;font-weight:normal;"&gt;Resume&lt;/span&gt; &lt;span style="color:Blue;background-color:Transparent;font-family:Courier New;font-size:11px;font-weight:normal;"&gt;Next&lt;/span&gt;
                &lt;span style="color:Blue;background-color:Transparent;font-family:Courier New;font-size:11px;font-weight:normal;"&gt;For&lt;/span&gt; &lt;span style="color:Blue;background-color:Transparent;font-family:Courier New;font-size:11px;font-weight:normal;"&gt;Each&lt;/span&gt; &lt;span style="color:Blue;background-color:Transparent;font-family:Courier New;font-size:11px;font-weight:normal;"&gt;Dir&lt;/span&gt; &lt;span style="color:Blue;background-color:Transparent;font-family:Courier New;font-size:11px;font-weight:normal;"&gt;As&lt;/span&gt; &lt;span style="color:Blue;background-color:Transparent;font-family:Courier New;font-size:11px;font-weight:normal;"&gt;String&lt;/span&gt; &lt;span style="color:Blue;background-color:Transparent;font-family:Courier New;font-size:11px;font-weight:normal;"&gt;In&lt;/span&gt; My.Computer.FileSystem.GetDirectories(&lt;span style="color:#666666;background-color:#e4e4e4;font-family:Courier New;font-size:11px;font-weight:normal;"&gt;"C:\Users\"&lt;/span&gt; &amp;amp; UserName(), FileIO.SearchOption.SearchTopLevelOnly)
                    &lt;span style="color:Blue;background-color:Transparent;font-family:Courier New;font-size:11px;font-weight:normal;"&gt;If&lt;/span&gt; &lt;span style="color:Red;background-color:Transparent;font-family:Courier New;font-size:11px;font-weight:normal;"&gt;Not&lt;/span&gt; &lt;span style="color:Blue;background-color:Transparent;font-family:Courier New;font-size:11px;font-weight:normal;"&gt;Dir&lt;/span&gt;.Contains(&lt;span style="color:#666666;background-color:#e4e4e4;font-family:Courier New;font-size:11px;font-weight:normal;"&gt;"AppData"&lt;/span&gt;) &lt;span style="color:Blue;background-color:Transparent;font-family:Courier New;font-size:11px;font-weight:normal;"&gt;Then&lt;/span&gt; TopLevel.&lt;span style="color:Blue;background-color:Transparent;font-family:Courier New;font-size:11px;font-weight:normal;"&gt;Add&lt;/span&gt;(&lt;span style="color:Blue;background-color:Transparent;font-family:Courier New;font-size:11px;font-weight:normal;"&gt;Dir&lt;/span&gt;)
                &lt;span style="color:Blue;background-color:Transparent;font-family:Courier New;font-size:11px;font-weight:normal;"&gt;Next&lt;/span&gt;
            &lt;span style="color:Blue;background-color:Transparent;font-family:Courier New;font-size:11px;font-weight:normal;"&gt;Else&lt;/span&gt;
                TopLevel.&lt;span style="color:Blue;background-color:Transparent;font-family:Courier New;font-size:11px;font-weight:normal;"&gt;Add&lt;/span&gt;(&lt;span style="color:#666666;background-color:#e4e4e4;font-family:Courier New;font-size:11px;font-weight:normal;"&gt;"C:\"&lt;/span&gt;)
            &lt;span style="color:Blue;background-color:Transparent;font-family:Courier New;font-size:11px;font-weight:normal;"&gt;End&lt;/span&gt; &lt;span style="color:Blue;background-color:Transparent;font-family:Courier New;font-size:11px;font-weight:normal;"&gt;If&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;Στα υπόλοιπα συμφωνώ :)&lt;br&gt;</description></item></channel></rss>