CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Raymond Lewallen

Framework Design, Agile Coach, President Oklahoma City Developers Group, Microsoft MVP C#, TDD, Continuous Integration, Patterns and Practices, Domain Driven Design, Speaker, VB.Net, C# and Sql Server

March 2005 - Posts

  • Performance Monitoring - Garbage Collection

    I see this topic quite a bit on newsgroups and in microsoft chats. People ask how and what to monitor to check out the performance of their application. First and foremost, I tell people to get CLRProfiler.
    The CLR Profiler includes a number of very useful views of the allocation profile, including a histogram of allocated types, allocation and call graphs, a time line showing GCs of various generations and the resulting state of the managed heap after those collections, and a call tree showing per-method allocations and assembly loads.
    This is an invaluable tool.

    This blog post topic isn't about the CLRProfiler though. This is about using PerfMon. When installing .Net, many, many new counters are added that allow you to get detailed and real-time information about the performance of your applications.  We are only going to talk about one set of those for monitoring garbage collections.  Launch PerfMon, and click the + to add new counters. In the Performance Object, select ".Net CLR Memory". It should look something similar to below:

    Select a specific application to monitor from the right. You see on my machine the managed applications that are running. I can select OMEA Reader and profile its memory and GC usage.

    I always use the following when doing checking the performance of the GC, along with their explanations:

    # Gen 0 Collections - This counter displays the number of times the generation 0 objects (youngest; most recently allocated) are garbage collected (Gen 0 GC) since the start of the application. Gen 0 GC occurs when the available memory in generation 0 is not sufficient to satisfy an allocation request. This counter is incremented at the end of a Gen 0 GC. Higher generation GCs include all lower generation GCs. This counter is explicitly incremented when a higher generation (Gen 1 or Gen 2) GC occurs. _Global_ counter value is not accurate and should be ignored. This counter displays the last observed value.

    Gen 0 Heap Size - This counter displays the maximum bytes that can be allocated in generation 0 (Gen 0); its does not indicate the current number of bytes allocated in Gen 0. A Gen 0 GC is triggered when the allocations since the last GC exceed this size. The Gen 0 size is tuned by the Garbage Collector and can change during the execution of the application. At the end of a Gen 0 collection the size of the Gen 0 heap is infact 0 bytes; this counter displays the size (in bytes) of allocations that would trigger the next Gen 0 GC. This counter is updated at the end of a GC; its not updated on every allocation.

    # Gen 1 Collections - This counter displays the number of times the generation 1 objects are garbage collected since the start of the application. The counter is incremented at the end of a Gen 1 GC. Higher generation GCs include all lower generation GCs. This counter is explicitly incremented when a higher generation (Gen 2) GC occurs. _Global_ counter value is not accurate and should be ignored. This counter displays the last observed value.

    Gen 1 heap size - This counter displays the current number of bytes in generation 1 (Gen 1); this counter does not display the maximum size of Gen 1. Objects are not directly allocated in this generation; they are promoted from previous Gen 0 GCs. This counter is updated at the end of a GC; its not updated on every allocation.

    # Gen 2 Collections - This counter displays the number of times the generation 2 objects (older) are garbage collected since the start of the application. The counter is incremented at the end of a Gen 2 GC (also called full GC). _Global_ counter value is not accurate and should be ignored. This counter displays the last observed value.

    Gen 2 heap size - This counter displays the current number of bytes in generation 2 (Gen 2). Objects are not directly allocated in this generation; they are promoted from Gen 1 during previous Gen 1 GCs. This counter is updated at the end of a GC; its not updated on every allocation.

    Large Object Heap Size - This counter displays the current size of the Large Object Heap in bytes. Objects greater than 20 KBytes are treated as large objects by the Garbage Collector and are directly allocated in a special heap; they are not promoted through the generations. This counter is updated at the end of a GC; its not updated on every allocation.

    # Bytes in all Heaps - This counter is the sum of four other counters; Gen 0 Heap Size; Gen 1 Heap Size; Gen 2 Heap Size and the Large Object Heap Size. This counter indicates the current memory allocated in bytes on the GC Heaps.

    Promoted memory from Gen 0 - This counter displays the bytes of memory that survive garbage collection (GC) and are promoted from generation 0 to generation 1; objects that are promoted just because they are waiting to be finalized are not included in this counter. This counter displays the value observed at the end of the last GC; its not a cumulative counter.

    Promoted memorty from Gen 1 - This counter displays the bytes of memory that survive garbage collection (GC) and are promoted from generation 1 to generation 2; objects that are promoted just because they are waiting to be finalized are not included in this counter. This counter displays the value observed at the end of the last GC; its not a cumulative counter. This counter is reset to 0 if the last GC was a Gen 0 GC only.

    So I added all of these counters, and opened up OMEA Reader. I started up the PerfMon to start tracking and then did an update for all my webfeeds in OMEA Reader. As soon as that completed, I waited about 5 seconds and then took this snapshot of the report view of my performance monitor:

    About a year ago, I spoke with a GC architect and he informed me that a very optimal ratio for generational GC's is 100-10-1 (gen 0-1-2). The app I'm profiling, under the circumstances and with resources available, doesn't reach that optimal level, but I've never profiled anything that does. This GC collection ratio is what you are primarily looking for from these counters. GC's are expensive and you'd like to see them kept at a minimum. You won't see a counter for LOH (large object heap) collections. They happen every time a Gen 2 collection occurs, so the collection counts are the same for both.

  • Billy Hollis in OKC April 4th

    Billy Hollis will be at the Oklahoma City .Net Developer's Group for our April 4th meeting.  For anybody in the area, please come by and hear him speak!

    Speaker and Topic: Billy Hollis / Windows Forms Custom Controls


     
    Billy Hollis is co-author of the first book ever published on Visual Basic.NET, VB.NET Programming on the Public Beta, from Wrox Press, as well as numerous other books and articles on .NET. At Microsoft’s request, Billy served as the co-instructor for all preparation sessions for Microsoft’s first .NET Developer Training Tour, thereby training over two hundred instructors who delivered this material world-wide.

  • Using sp_MSforeachtable

    On the topic of commands I haven't used much since the last migration from 7.0 to 2000, while using different methods to test moving data from 2000 to 2005, I come across having to deal with constraints and triggers.  Lets say you script out a database structure, indexes, triggers and constraints and run that in Sql2k5.  I have a little tool called sqlDump that I use to export data without having to create a DTS package.  Now when you want to load up all the data again, you are going to run into some constraint violations and insert/update triggers are going to start firing off.  Instead of going in to delete all those constraints and triggers only to add them back later, just disable them all using a system stored procedure called sp_MSforeachtable.

    sp_MSforeachtable will loop through all the tables in the database, performing a command.  In our case above, we want to run 2 commands: 1 to set NOCHECK on all CONSTRAINTs, and another to DISABLE all TRIGGERs.

    sp_msforeachtable "ALTER TABLE ? NOCHECK CONSTRAINT all"
    sp_msforeachtable "ALTER TABLE ? DISABLE TRIGGER  all"

    The '?' serves as a place holder for the table name.  Once your data is loaded, just turn everything back on:

    sp_msforeachtable "ALTER TABLE ? CHECK CONSTRAINT all"
    sp_msforeachtable "ALTER TABLE ? ENABLE TRIGGER  all"

    You can be a bit fancier and run more than 1 command per call to sp_msforeachtable (limited to 3 commands) by passing in parameters of commands you want to execute per table.

    sp_msforeachtable @command1="ALTER TABLE ? CHECK CONSTRAINT all", @command2="ALTER TABLE ? ENABLE TRIGGER  all"

    Another great use for this sp is for DBCC commands on tables or database, as there is also a sp_MSforeachdb stored procedure too. 

    sp_msforeachtable "dbcc checktable ('?')"
  • My top 9 favorite bloggers

    Could not decide on 10 so it became 9.  This list is only for individual blogs and not group blogs.  Also, any entries of any members or family members of those associated with CodeBetter are ineligible to participate in this contest, or something like that.

    Here is my list, in alphabetic order:

    Brad Abrams
    K. Scott Allen
    Matt Berther
    Martin Fowler
    Scott Galloway
    Scott Hanselman
    Rico Mariani
    Cyrus Najmabadi
    Roy Osherove

    These are guys I read every day and never click the "mark all as read" on.  These 9 blogs make up 60% of my flagged posts that are flagged to go back and read again later/this is good stuff/keep for future reference.  I have over 200 blogs in my OPML, so that 60% from 9 blogs carries some heavy weight.

    So who are your favorite bloggers?
  • Update your compatibility level on your databases

    It has been awhile since we've all upgraded from 7.0 to 2000 Sql Server.  Just a reminder, don't forget to update your compatibility levels, if applicable, when moving your databases from 2000 (version 8.0) to 2005 (version 9.0) in order to take advantage of the latest features and enhancements.  I keep forgetting to do this when moving my databases and discover it every time I try to PIVOT code (topic coming soon using 'pubs' database as an example) and it throws an error.

    The syntax is as follows:
    Exec sp_dbcmptlevel 'pubs', '90'

    If you move the pubs database from 2000 to 2005, that will change its compatibility level to 2005.  Values accepted are:
    70 = SQL Server 7.0.
    80 = SQL Server 2000.
    90 = SQL Server 2005.

    FYI, I have moved 8 databases from 2000 to 2005 Beta2 with no problems.

  • Structured Query Language (SQL) Request

    /*
    This is straight from the query window in Sql2k5, that is why these lines are commented out.  I'm using the pubs database as an example.
    */

    USE Pubs

    /*
    Every row in Titles has at least one or more matches in Roysched.
    */

    SELECT     T.*, RS.*
    FROM         titles T INNER JOIN
                          roysched RS ON RS.title_id = T.title_id

    /*
    Notice that the query returns 86 rows and title_id is duplicated. I don't want duplicates, I want to return only the Royalty Schedule that has the highest value in lorange for each title. How to do it?
    */

    SELECT T.*, RS.*
    FROM Titles T
        INNER JOIN
            (
            SELECT TOP 1 *
            FROM RoySched
            ) RS ON RS.title_id = T.title_id

    /*
    This doesn't work because it only returns 1 row from the child table.  Causing the INNER JOIN to match only one row in the parent table.  How to return the best match for all rows in the parent table?
    */

    --Identical to last query except for the WHERE clause in the sub-query
    SELECT T.*, RS.*
    FROM Titles T
        INNER JOIN
            (
            SELECT TOP 1 *
            FROM RoySched RsNest
            WHERE RsNest.title_id = T.title_id
            ORDER BY RsNest.LoRange DESC
            ) RS ON RS.title_id = T.title_id

    /*
    This doesn't work because we can't reference the parent table inside the sub-query. What can be done? Here is one option:
    */

    SELECT T.*, RS.*
    FROM Titles T
        INNER JOIN RoySched RS ON
            RS.title_id = T.title_id AND
            RS.lorange =
            (
                SELECT MAX(LoRange)
                FROM RoySched
                WHERE title_id = T.title_id
            )

    /*
    Now we have the data we want, but notice the sub-query in the Execution Plan.  It is performing an index seek which is good, but the sub-query is executing once for every row in the parent, which slaughters performance when the parent table has 2,000,000 rows in it. The following pseudo-code is the feature I would like to see in SQL:
    */

    SELECT T.*, RS.*
    FROM Titles T
        INNER JOIN TOP 1 RoySched RS ORDER BY LoRange ON
            RS.title_id = T.title_id

    /*
    First of all, note that the code is not saying "join to the first row in RoySched", it is saying "join to the first T.title_id match in RoySched".

    The Top-n clause let's me say I only want the best match, and the ORDER BY clause allows me to guarantee that the best match is returned. Even if the ORDER BY clause required a poorly optimized sort operation, it would only have to be done once and under most circumstances would be far more performant than 2,000,000 highly optimized sub-queries; and once you built the appropriate index on your child table, the sort would be very fast anyway.

    Now at this point you're probably asking, why not just select the title_id and max(lorange) from RoySched in to a temp table or table variable and join from it? While the example shown above is the closest I could find in Pubs that simulates my issue, it is still much simpler than the logic required in my production scenario.  Because of a production requirement of wildcards in the criteria, extensive processing is required before I can build the temp table.  This however is the best performing option so far and is what I'm using.  If the solution shown above were available I could eliminate all the pre-processing and save significant processing time.
    */
  • Know the contents of the system tables in your database

    If you are working in Sql Server, one of the most important things you can learn is the contents of the system tables within your database. Objects are stored in sysobjects. Stored procedures are stored in syscomments. Column names for tables are stored in syscolumns by object id (just ID in the database), and there are others. Please go explore them, just don't change anything.

    This came up because I was talking about pivot with someone and how a cool little keyword helps to do what takes a bunch of code in current release versions of Sql Server. I'm going to save pivot/unpivot topic for later this week. This friend-o-mine had hard coded tables that "pivoted" tables around for his purposes, with the column names of the table to pivot being hardcoded (isn't that confusing?). Now, Sql Server 2005 solves this scenario for us, as far as having to code it manually. But for those of you who are currently in 2000 or 7.0, you don't need to hardcode those column names for each row in a temp table. They already exist in the syscolumns table of your database. *NOTE* This only works well if you are actually using all the columns in the table to pivot. Outside of pivoting information, pulling this information from the syscolumns table still provides useful in many other scenarios, most commonly in data dictionary code.
    Take a look at the following:
    declare @tableName as sysname
    set @tableName = 'Orders'

    select [name] As ColumnName from syscolumns where [id] =
    (select id from sysobjects where [name] = @tableName
        and xtype = 'U')
    order by colorder

    The output are the column names, in physical order as they occur in the table itself, as follows:
    ColumnName
    -------------------
    OrderID
    CustomerID
    EmployeeID
    OrderDate
    RequiredDate
    ShippedDate
    ShipVia
    Freight
    ShipName
    ShipAddress
    ShipCity
    ShipRegion
    ShipPostalCode
    ShipCountry

    (14 row(s) affected)

    In a few days, I'll show you the what PIVOT and UNPIVOT do, and I'll show you the really hard and crappy way you have to do it in Sql Server 2000 and earlier (or at least the hard and crappy way I do it).
  • The book I just finished - XP Pocket Guide


    I recently finished reading the Extreme Programming Pocket Guide.  I don't think you will find a more precise, to the point, compact package of XP anywhere like you find in this little book.  It is only 80 pages and can be read in about 3 hours.  In some near future posts coming up, this book will be my primary reference for the direct, no-fluff, short explanations of XP methodologies.  I do not, however, recommend reading this book first if you haven't delved into XP much.  I would recommend starting with Extreme Programming Explained: Embrace Change by Kent Beck, or the 2nd Edition of the same by Kent Beck and Cynthia Andres. I have not read the 2nd edition.  Planning Extreme Programming by Kent Beck and Martin Fowler is another great book to start with.

    If you are giving talks, presentations or dicussions on XP, this little pocket guide is a must have.  Thanks to Dino Esposito for turning me on to it.
    Extreme Programming (XP) is a radical new approach to software development that has been accepted quickly because its core practices---the need for constant testing, programming in pairs, inviting customer input, and the communal ownership of code---resonate with developers everywhere. Although many developers feel that XP is rooted in commonsense, its vastly different approach can bring challenges, frustrations, and constant demands on your patience. Unless you've got unlimited time (and who does these days?), you can't always stop to thumb through hundreds of pages to find the piece of information you need. The Extreme Programming Pocket Guide is the answer. Concise and easy to use, this handy pocket guide to XP is a must-have quick reference for anyone implementing a test-driven development environment. The Extreme Programming Pocket Guide covers XP assumptions, principles, events, artifacts, roles, and resources, and more. It concisely explains the relationships between the XP practices. If you want to adopt XP in stages, the Extreme Programming Pocket Guide will help you choose what to apply and when.
  • A new book I'm about to read - eXtreme .Net



    Just got this new book in the mail today, eXtreme .NET : Introducing eXtreme Programming Techniques to .NET Developers.  I'm very excited to start reading this book, as it will be my fifth on the topic of XP, but my first book that actually focuses on XP in a managed code environment.  It was published in December 2004, so its only been available for a few months.  If anybody else out there has read this book, I would love to know what you thought about it.
    eXtreme .NET shows developers and team leaders how to incorporate eXtreme programming (XP) practices with .NET-connected technologies to create high quality, low-cost code that will build better software. This practical, realistic guidebook systematically covers key elements of XP methodology in the specific context of the .NET Framework, Visual Studio .NET, Microsoft Visual C#, and related Microsoft .NET-enabled applications. Leading .NET and XP mentor Dr. Neil Roodyn covers planning, task definition, test-driven development, user interfaces, refactoring, spiking, pair programming, and much more. Dr Neil offers field-proven advice for everything from automating builds to integrating third-party libraries. He also incorporates valuable exercises and presents a start-to-finish case study that shows exactly how XP and Microsoft .NET interoperate throughout an entire development project.
  • A few takes on polymorphism

    A couple of days ago I talked about polymorphism.  I can't even begin to tell you what all discussions I've had since putting this up.  Let me start by saying this:  this post, as all my posts on OOP principles, are not intended to cover the complete aspect of the principle, and this applies most heavily to polymorphism.  Polymorphism can be a very complex concept and difficult to understand in certain situations.

    1) "Polymorphism is coupled with inheritance." I made this statement in my post. Looking back, I probably shouldn't have because the statement is not true. More correctly, polymorphism can be coupled with inheritance, but inheritance is not requierd to gain polymorphic behavior.

    2) Please read this post by Daniel Moth.  He counters on my statement discussed in #1 above, and he is correct.  This is a very good post, even if I did peeve him off little :)  Daniel's a very smart guy and I'm proud and appreciative to have his input on my postings.

    3) Now that you've read Daniel's post... and I've also had this discussion via email with someone else who is very OOP knowledgable like Daniel is, and that is method overloading.  In my opinion, and in the opinion of others, overloading is a form of polymorphism, also referred to as compile-time polymorphism and parametric polymorphism.  I'll leave it to you to form your own opinion.

    4) My polymorphism post was incomplete, and I apologize for that.  The post was meant to only give a very basic understanding of polymorphism in the .Net world to those who have never dealt with the aspect of it before.  Looking back, I should have discussed more about it and in greater depth.  To be honest, I left out other aspects of polymorphism (interface is the biggie) as not to unload too much at once and confuse anybody new to the idea.

    Again, the post was not intended to give you the entire picture of polymorphism.  There are entire chapters of books dedicated to the subject, and I certainly wasn't going to attempt to do that in one blog post.  I merely intended to give a broad picture in how it applies most commonly to managed code (.Net).  Please do your own research, google the topics you read and form your own opinions.  This is true for all of my blog posts.
  • We really do help

    I got this comment yesterday on one of my posts:

    Hi! Thank you so much for this blog....I've learned so much from you guys...I'm a programmer...having problems...in considering myself..if I'm a good programmer or not...I always compare myself to my colleagues...they are really good in programming and switching languages...I always try to do their programs...and when i fail running it I always feel disappointed and tend to think that I'm not a good programmer and I should give up in this course...

    For starters, we are all appreciative of our readers and hope to continue to provide you all with helpful, knowledgeable and dynamic content.  I know there are many, many blogs out there you can go to for tips, tricks, advice, help and information, and we, CodeBetter, are glad that some of you come here.  Unlike many of the sites, you won’t find ads here, we don’t charge anybody and we aren’t trying to make any money or get anything in return from our readers other than your honest and continuous feedback.  We do this for the simple fact we want people to know what we know, in hopes that it will make you a better software developer/database administrator/computer programmer/lingerie supermodel/software architect/monk/systems analyst or whatever your profession may be.

    Secondly, to this reader in particular, if you want to write software, then you should.  Everybody learns at a different pace.  There are many different languages out there.  There are databases, business layers, interface layers…. all different aspects of a software lifecyle that you can be involved in.  We’ve all been at the beginning, except maybe Sahil, he came from the womb with the first 2 chapters of his first book already written.  Point is, everybody has a starting point.  Everybody has obstacles to overcome.  Everybody has worked with people who were much better than we are.  We learn from it.  We learn from others.  I can tell you this: working with those people, as long as they are helpful towards you and your questions, is probably the best thing you can be around right now.  They can help guide you and help you make good, smart decisions.  They can explain to you why something doesn’t work or does work the way it has been written.

    Best I can tell you is to do what you love.  I’m fortunate enough to get paid to do something that is my biggest hobby – software development.  If I were driving a forklift during the day, I’d still be writing software at home during the night.  I can only hope that someday you find yourself in the same type situation.  Don’t give up; its a long road for everyone, no matter what your level of expertise is.  Technology is ever changing.  3 years ago, there was no such thing as an expert in .Net.

    If its what you want to do, stick with it, and always feel free to come to CB and send any one of us an email asking for help or advice.  If we don’t know the answer, we’ll find you someone who does.

  • Polymorphism Defined

    Finally, the final of the 4 posts on object-oriented principles for those of you just coming to .Net from non-OOP languages.  Data Abstraction, Encapsulation and Inheritance we have already discussed.  Now we will talk about polymorphism.

    Polymorphism means one name, many forms.  Polymorphism manifests itself by having multiple methods all with the same name, but slighty different functionality.  Updated statement: Polymorphism can be coupled with inheritance, but inheritance is not a requirement to gain polymorphic behaviors.  Because of this, it can be difficult to fully grasp the full potential of polymorphism until you get some practice with it and see exactly what happens under different scenarios.  We’re only going to talk about polymorphism, like the other topics, at the basic level. 

    There are 2 basic types of polymorphism.  Overridding, also called run-time polymorphism, and overloading, which is referred to as compile-time polymorphism.  This difference is, for method overloading, the compiler determines which method will be executed, and this decision is made when the code gets compiled. Which method will be used for method overriding is determined at runtime based on the dynamic type of an object.

    Let’s look at some code:

    ' Base class for library assets

    Public MustInherit Class LibraryAsset

     

        ' Default fine per day for overdue items

        Private Const _finePerDay As Double = 1.25

     

        ' Due date for an item that has been checked out

        Private _dueDate As DateTime

        Public Property DueDate() As DateTime

            Get

                Return _dueDate

            End Get

            Set(ByVal Value As DateTime)

                _dueDate = Value

            End Set

        End Property

     

        ' Calculates the default fine amount for an overdue item

        Public Overridable Function CalculateFineTotal() As Double

            Dim daysOverdue As Int32 = CalculateDaysOverdue()

            If daysOverdue > 0 Then

                Return daysOverdue * _finePerDay

            Else

                Return 0.0

            End If

        End Function

     

        ' Calculates how many days overdue for an item being returned

        Protected Function CalculateDaysOverdue() As Int32

            Return DateDiff(DateInterval.Day, _dueDate, DateTime.Now())

        End Function

     

    End Class

     

    ' Magazine class that inherits LibraryAsset

    Public NotInheritable Class Magazine

        Inherits LibraryAsset

     

    End Class

     

    ' Book class that inherits LibraryAsset

    Public NotInheritable Class Book

        Inherits LibraryAsset

     

        ' This is morphing the CalculateFineTotal() function of the base class.

        ' This function overrides the base class function, and any call

        '   to CalculateFineTotal from any instantiated Book class will

        '   use this function, not the base class function.

        ' This type of polymorphism is called overriding.

        Public Overrides Function CalculateFineTotal() As Double

            Dim daysOverdue As Int32 = CalculateDaysOverdue()

            If daysOverdue > 0 Then

                Return daysOverdue * 0.75

            Else

                Return 0.0

            End If

        End Function

     

    End Class

     

    ' AudioCassette class that inherits LibraryAsset

    Public NotInheritable Class AudioCassette

        Inherits LibraryAsset

     

        ' This is morphing the CalculateFineTotal() function of the base class.

        ' This is morphing the CalculateFineTotal(double) function of the

        '   audiocassette class.

        ' This function overrides the base class function, and any call

        '   to CalculateFineTotal() from any instantiated AudioCassette

        '   Class will use this function, not the base class function.

        ' This type of polymorphism is called overloading and overriding.

        Public Overloads Overrides Function CalculateFineTotal() As Double

            Dim daysOverdue As Int32 = CalculateDaysOverdue()

            If daysOverdue > 0 Then

                Return daysOverdue * 0.25

            Else

                Return 0.0

            End If

        End Function

     

        ' This is morphing the CalculateFineTotal() function of the

        '   audiocassette class.

        ' This type of polymorphism is called overloading.

        Public Overloads Function CalculateFineTotal(ByVal finePerDay As Double) As Double

            Dim daysOverdue As Int32 = CalculateDaysOverdue()

            If daysOverdue > 0 AndAlso finePerDay > 0.0 Then

                Return daysOverdue * finePerDay

            Else

                Return 0.0

            End If

        End Function

    End Class

     

    You see our library asset class.  Pay attention to the overridable function CalculateFineTotal().  In LibraryAsset, we have defined the default functionality for this method that any derived classes can use.  Any class derived from LibraryAsset can use this default behavior and calculate fines based on the default implementation of $1.25 per day late.  This is true for our Magazine class.  We didn’t override the function so when late fees are calculated for late magazine returns, it will use the default implementation.

    Now look at the book class.  We have overridden the CalculateFineTotal to use a different value when determining late fees.  The overrides keywork in VB tells the caller that any method call will use the virtual method found in Book, not the default implementation found in LibraryAsset.  We have implemented runtime polymorphism – method overriding.

    Lets move on to AudioCassette.  Here we have the same method overriding we found in the book class.  Fines are calculated based on $0.25 per day.  Notice we’ve added something extra.  We’ve added the Overloads keywork to our function and to a new function with the same name, except the new function now accepts a parameter.  Now the caller can call either method, and depending on whether or not a parameter is passed, that determines with method will be executed.  Notice we do not include the overrides keywork in the 2nd function with a parameter.  This is because not method exists in LibraryAsset with that same signature (accepting a parameter of type double).  You can only override methods with the same signature in a base class.

    Now lets look at some code that creates all these library items and checks them in and cacluates our fines based on returning them 3 days late:

    Public Class Demo

     

        Public Sub Go()

            ' Set the due date to be three days ago

            Dim dueDate As DateTime = DateAdd(DateInterval.Day, -3, Now())

            ReturnMagazine(dueDate)

            ReturnBook(dueDate)

            ReturnAudioCassette(dueDate)

        End Sub

     

        Public Sub ReturnMagazine(ByVal dueDate As DateTime)

            Dim myMagazine As LibraryAsset = New Magazine

            myMagazine.DueDate = dueDate

            Dim amountDue As Double = myMagazine.CalculateFineTotal()

            Console.WriteLine("Magazine: {0}", amountDue.ToString())

        End Sub

     

        Public Sub ReturnBook(ByVal dueDate As DateTime)

            Dim myBook As LibraryAsset = New Book

            myBook.DueDate = dueDate

            Dim amountDue As Double = myBook.CalculateFineTotal()

            Console.WriteLine("Book: {0}", amountDue.ToString())

        End Sub

     

        Public Sub ReturnAudioCassette(ByVal dueDate As DateTime)

            Dim myAudioCassette As AudioCassette = New AudioCassette

            myAudioCassette.DueDate = dueDate

            Dim amountDue As Double

            amountDue = myAudioCassette.CalculateFineTotal()

            Console.WriteLine("AudioCassette1: {0}", amountDue.ToString())

            amountDue = myAudioCassette.CalculateFineTotal(3.0)

            Console.WriteLine("AudioCassette2: {0}", amountDue.ToString())

        End Sub

     

    End Class

    The output will look like the following:

    Magazine: 3.75
    Book: 2.25
    AudioCassette1: 0.75
    AudioCassette2: 9

    You can see how all of our output was different, based on the method that was executed.  We created a new Magazine, which is a type of LibraryAsset.  That is why the instantiation says “myMagazine As LibraryAsset”.  However, since we actually want a magazine, we create a “New Magazine”.  Same thing with book.  For Book, its a little bit more tricky.  Since we created a Book of the type LibraryAsset, this is where the polymorphism comes into play.  Book overrides the CalculateFineTotal of LibraryAsset.  Audiocassette is a little bit different.  It actually extends the implementation of LibraryAsset by including an overloaded function for CalculateFineTotal().  If we weren’t going to use the function that took a parameter, we would create it the same way we created the Book and Magazine classes.  But in order to use the overloaded function, we have to create a new AudioCassette of the type AudioCassette, because LibraryAsset doesn’t support the overloaded function.

    Only the Magazine used the default method found in the base class.  Book and AudioCassette used their own implementations of the method.  Also, at compile time, the decision was made which method would be used when we calculate amountDue for the AudioCassette class.  The first call used the 1st method in AudioCassette without parameters.  The 2nd call used the 2nd method with a parameter.

    Currently listening to: Paul Revere - Beastie Boys

  • Catching up... what a chore

    198 unread blog posts this morning.  That’s a good Monday.  Must have been decent weather around the country this weekend and lots of people were outside.  Usually runs closer to 300 when I come in on Mondays.  This is the biggest reason I don’t like Monday mornings.  I spend the weekends with my family.  I don’t read blog posts, don’t work on code (ok, maybe a little).  The only thing I do computer related is either play Enemy-Territory or read books.  I can guarantee that for the most part, like every Monday, I’m going to be doing a mass “Mark all as read” on these blog posts.  I don’t want to unsubscribe from any of them, but dang… its like coming to work Monday and having to read the Saturday newspaper, Sunday newspaper AND Monday newspaper all in one hour.

    Currently listening to:Fade Into You(Live) - Flickerstick

  • BlogJet feature request

    Recently, us folks over here aquired BlogJet and licenses for each of us.  A wonderful blogging tool.  I have been very happy with it and its features.  I do have one feature request, that probably doesn't apply for most of us.  I want to be able to queue up completed blog postings for publishing at a later date.  Sometimes on weekends I get a bunch of blog posts written all at once.  Since I'm always connected to the internet, why not have completed, unposted blog posts queued up with a publishing date?  Does this save me time? No.  Money?  No.  Does it really do anything special for me?  No.  I just like to have stuff to play with :)

    Right now I have finished my final post on OOP principles and a blog post on boxing/unboxing to be published in a few days.  Its no big deal to bring those up and just publish them when the time is right.  Where this feature would come in handy is on a new series I am starting on this next week, that will continue for the next 3 months.  I will be covering, per blog post, each of aspects of XP methodologies, one per week.  I might write 3 or 4 of them in one sitting.  I would like to be able to say "publish this one on March 18th, the next one on March 25th, etc"  and write them all at once and know they are finished and ready to publish.  Yes, you can save your posts as drafts and bring them up and publish them later, but I actually do have unfinished drafts and keep seperate folders for drafts and completed/unpublish because you have to publish by actually bringing up the post and clicking a button to publish.  I'm extremely lazy and what to automate the publishing of posts that belong to a series, such as my upcoming XP articles.
  • Asp.Net Page Life Cycle

    I found this wonderful file floating on my hard drive.  Its credit goes to Leon Andrianarivony.  I have looked and looked for a site of his to link up to and cannot find one.  Either way, he’s provided this wonderful drawing on the life cycle of an Asp.Net page.  Also noted are differences in v1.1 and v2.0 life cycles.

    Currently listening to: Renegade Master [Fatboy Slim Old Skool Mix] - Fatboy Slim

More Posts Next page »