FORTH-ICS / TR 381 July 2006 'An Informal Proof on the Contradictory

Total Page:16

File Type:pdf, Size:1020Kb

FORTH-ICS / TR 381 July 2006 'An Informal Proof on the Contradictory FORTH-ICS / TR 381 July 2006 ‘An Informal Proof on the Contradictory Role of Finalizers in a Garbage Collection Context’ Anthony Savidis ABSTRACT Garbage collection in OOP languages provides facilities to hook code that is executed upon object finalization. Semantically, the point in time that finalizer code is executed is not determined by the application logic, but by the garbage collection system. This fact renders a potential mismatch, since application-specific code, i.e. the finalizer implementation, normally affecting program state and control flow, is called automatically at a point in time that is indifferent to the application semantics. Although an analogous situation is observed in event-based systems, since event processors are called-back asynchronously by the underlying system, there is a fundamental difference: while event generation is essentially nondeterministic, originated from external event sources, object destruction is a semantic action that is always decided by applications. In summary, the mismatch is that although applications decide if and when destruction occurs, the garbage collector is responsible to commit the relevant code. We prove that this mismatch is due to the contradictory presence of finalizers in a garbage collection context. Page 1 Introduction Preface This technical report has been also motivated by requests from colleagues to report the informal proof for the contradictory role of finalizers in garbage collection, simply relying on informal common software engineering principles. Additionally, the reporting has been also motivated by arguments that came to my attention via personal communication that either the claim cannot be proved (I assume that “cannot prove” arguments are subject to formal proofs, so such arguments are more weak than the claim they attack), or that the claim is trivial requiring essentially no proof (intuitively true, but does not explain why Java, C#, Python, just to name a few languages, still adopt finalizers). Throughout this brief report we use the term “object cancellation” as a synonym to destruction, though without the memory disposal connotation. Our proof relies on the thesis that application objects are cancelled only due to conditions inherent in the application semantics, being evaluated as part of the program control-flow. As we discuss at the end, although there is an exception to this rule, it is better handled with a programming wrap-around rather than by relying on finalizers. Problems with finalizers and the severe confusion they may cause due to interference of the garbage collector with the application semantics in unpredictable ways are very well known, not limited only to novice programmers,. A few representative articles are provided in the references section [1,2,3,4,5,6,7,8] being indicative of the relevant problem. Since we are not aware of a proof or more elaborated statement exposing the contradictory role of finalizers in a garbage collection context, we provide one in this report. Primary thesis Objects are cancelled at runtime if and only if the application state implies that the relevant destruction conditions are met, decided always deterministically and as part of the application semantics within the normal program control flow. A. Savidis, 2006, Finalizers and garbage collection, TR_381_2006, ICS-FORTH Page 2 Following this position, all corresponding cancellation actions should be appropriately orchestrated by the initiative of the application, while such actions are expected to apply at runtime the particular application policy for instance cancellation. Additionally, these application actions may be committed instantly or later, depending on the application logic itself. On the other hand, automatic memory disposal usually takes place only when there is no external reference (i.e. program variable) to an object. In this sense, the logic for automatic memory disposal being system initiated is semantically indifferent to the application logic for object cancellation. Next we formulate our basic argument, followed by the proof that automatic garbage collection and finalizers semantically contradict. Argument Finalizers are not needed or else garbage collection becomes equivalent to manual memory management, aka, finalizers semantically contradict with the notion of garbage collection. Proof of the argument The proof is applied by abduction. Let’s assume that the language provides a finalizer function where programmers may encapsulate code to be called just before the memory of an object is automatically collected. In this case there are two possible programming alternatives: I. Either programmers totally dismiss the finalizer function, handling cancellation manually when the application-oriented conditions are met, calling special- purpose cancellation member functions; II. Or programmers utilize the finalizer to inject code that actually implements the application-oriented cancellation logic. If case (I) holds our argument becomes self-evident. However, if case (II) holds there are two possible options: a. The application logic deterministically requires that instance cancellation actions occur when it is decided to do so, and only if the relevant semantic conditions are met. Consequently, to accommodate this demand, programmers should explicitly A. Savidis, 2006, Finalizers and garbage collection, TR_381_2006, ICS-FORTH Page 3 write code to cause the garbage collector instantly collect the instance, such as by nullifying the last remaining references, at the exact points of code where the control flow implies that the cancellation conditions are met. Notably, it may not always be certified that collection will follow immediately reference nullification, meaning that finalizers are not a guaranteed method for implementing cancellation policies with precise timing. b. Else, it is of no semantic importance as to when the cancellation actions may actually take place. Apparently, it does not actually matter at what point during runtime the finalizer blocks are committed. In the (a) case, the automatic garbage collection benefit practically evaporates, since explicitly nullifying instance references, to actually cause object collection and finalizer invocation, is equivalent to explicit memory disposal, i.e. delete in languages without garbage collection. Hence, the automatic garbage collection becomes essentially analogous to manual memory disposal. In the (b) case, if the runtime point in which cancellation actions are performed is indifferent to the application semantics, we may execute the cancellation code exactly after normal application termination, meaning the cancellation actions become semantically nop to the application logic itself. However, in such a case, the semantic effect of the finalizer is apparently equivalent to that of an empty finalizer, meaning no finalizer is essentially needed. Resolving exceptions A viable exception to our thesis accounts to a very specific scenario where object finalizations should take place outside of the normal program control-flow: dynamic allocation of instances, within blocks that may be partially executed when exit is caused due to exception handling (i.e. stack unwinding). For instance, let’s consider the following code snippet, representative of blocks that encompass the instantiation, use and cancellation of instances of various classes. For clarity, we assume a single instance. A. Savidis, 2006, Finalizers and garbage collection, TR_381_2006, ICS-FORTH Page 4 f() { X x = new X Use(x) Cancel(x) return whatever; } In this example, if the execution of the statements following the creation of x, including Use(x) but before Cancel(x), causes an exception that is not handled before Cancel(x), the function will be exited without ever calling Cancel(x). Hence, without the support of finalizers in garbage collection, it may look that there is no way to ensure in such a scenario that the code for cancelling x is always called before x gets collected. Next we prove by implementation the trivial, that finalization even for such exceptional situations can be programmed in a straightforward manner. Let’s consider an alternative implementation shown below, reflecting programmer’s awareness of the fact that exceptions may be raised due to calls made inside this function. f() { X x = new X try { Use(x) } catch Exception e { Cancel(x) rethrow; Å Abnormal function exit with exception propagation } Cancel(x) return whatever; Å Graceful function return } The extra code serves graceful cancellation, handling any type of exception followed by immediate re-raising for subsequent handling by an appropriate calling function. Effectively, this seems to turn the updated version of our code without finalizers equivalent to the previous one assuming finalizers are supported. The only apparent issue concerns the required duplication of the cancellation statement Cancel(x). However, once our scenario is generalized to assume multiple instances that may be created and used locally within a block, we may observe that it is not certified that all such instances would have been created when an exception is raised. Hence, we should manage to perform cancellation calls within the catch block only for those instances that were actually instantiated. Finally, we should cope with the possibility A. Savidis, 2006, Finalizers and garbage collection, TR_381_2006, ICS-FORTH Page 5 that instantiations may naturally raise exceptions, meaning we have to place new expressions within the basic try block. Reflecting
Recommended publications
  • Assertions, Pre/Post- Conditions and Invariants
    9/14/12 Assertions, pre/post- conditions and invariants Section 2.1 in Walls and Mirrors Section 4.5 Rosen Programming as a contract n Specifying what each method does q Specify it in a comment before method's header n Precondition q What is assumed to be true before the method is executed q Caller obligation n Postcondition q Specifies what will happen if the preconditions are met q Method obligation 1 9/14/12 Class Invariants n A class invariant is a condition that all objects of that class must satisfy while it can be observed by clients n What about Points in Cloud? q boundaries? q center? What is an assertion? n An assertion is a statement that says something about the state of your program n Should be true if there are no mistakes in the program //n == 1 while (n < limit) { n = 2 * n; } // what could you state here? 2 9/14/12 What is an assertion? n An assertion is a statement that says something about the state of your program n Should be true if there are no mistakes in the program //n == 1 while (n < limit) { n = 2 * n; } //n >= limit //more? What is an assertion? n An assertion is a statement that says something about the state of your program n Should be true if there are no mistakes in the program //n == 1 while (n < limit) { n = 2 * n; } //n >= limit //n is the smallest power of 2 >= limit 3 9/14/12 assert Using assert: assert n == 1; while (n < limit) { n = 2 * n; } assert n >= limit; When to use Assertions n We can use assertions to guarantee the behavior.
    [Show full text]
  • Solid Code Ebook
    PUBLISHED BY Microsoft Press A Division of Microsoft Corporation One Microsoft Way Redmond, Washington 98052-6399 Copyright © 2009 by Donis Marshall and John Bruno All rights reserved. No part of the contents of this book may be reproduced or transmitted in any form or by any means without the written permission of the publisher. Library of Congress Control Number: 2008940526 Printed and bound in the United States of America. 1 2 3 4 5 6 7 8 9 QWT 4 3 2 1 0 9 Distributed in Canada by H.B. Fenn and Company Ltd. A CIP catalogue record for this book is available from the British Library. Microsoft Press books are available through booksellers and distributors worldwide. For further infor mation about international editions, contact your local Microsoft Corporation office or contact Microsoft Press International directly at fax (425) 936-7329. Visit our Web site at www.microsoft.com/mspress. Send comments to [email protected]. Microsoft, Microsoft Press, Active Desktop, Active Directory, Internet Explorer, SQL Server, Win32, Windows, Windows NT, Windows PowerShell, Windows Server, and Windows Vista are either registered trademarks or trademarks of the Microsoft group of companies. Other product and company names mentioned herein may be the trademarks of their respective owners. The example companies, organizations, products, domain names, e-mail addresses, logos, people, places, and events depicted herein are fictitious. No association with any real company, organization, product, domain name, e-mail address, logo, person, place, or event is intended or should be inferred. This book expresses the author’s views and opinions. The information contained in this book is provided without any express, statutory, or implied warranties.
    [Show full text]
  • Sound Invariant Checking Using Type Modifiers and Object Capabilities
    Sound Invariant Checking Using Type Modifiers and Object Capabilities. Isaac Oscar Gariano Victoria University of Wellington [email protected] Marco Servetto Victoria University of Wellington [email protected] Alex Potanin Victoria University of Wellington [email protected] Abstract In this paper we use pre existing language support for type modifiers and object capabilities to enable a system for sound runtime verification of invariants. Our system guarantees that class invariants hold for all objects involved in execution. Invariants are specified simply as methods whose execution is statically guaranteed to be deterministic and not access any externally mutable state. We automatically call such invariant methods only when objects are created or the state they refer to may have been mutated. Our design restricts the range of expressible invariants but improves upon the usability and performance of our system compared to prior work. In addition, we soundly support mutation, dynamic dispatch, exceptions, and non determinism, while requiring only a modest amount of annotation. We present a case study showing that our system requires a lower annotation burden compared to Spec#, and performs orders of magnitude less runtime invariant checks compared to the widely used ‘visible state semantics’ protocols of D, Eiffel. We also formalise our approach and prove that such pre existing type modifier and object capability support is sufficient to ensure its soundness. 2012 ACM Subject Classification Theory of computation → Invariants, Theory of computation → Program verification, Software and its engineering → Object oriented languages Keywords and phrases type modifiers, object capabilities, runtime verification, class invariants Digital Object Identifier 10.4230/LIPIcs.CVIT.2016.23 1 Introduction Object oriented programming languages provide great flexibility through subtyping and arXiv:1902.10231v1 [cs.PL] 26 Feb 2019 dynamic dispatch: they allow code to be adapted and specialised to behave differently in different contexts.
    [Show full text]
  • The Contract Pattern Permission Granted to Copy for Plop ’97 Conference
    Copyright 1997, Michel de Champlain The Contract Pattern Permission granted to copy for PLoP ’97 Conference. All other rights reserved. Michel de Champlain Department of Computer Science University of Canterbury, Christchurch, New Zealand [email protected] http://www.cosc.canterbury.ac.nz/~michel 12 August 1997 Abstract This paper describes the Contract pattern, an idiom that lets you apply assertions to guarantee pre-conditions and post-conditions of methods and invariants on the state of objects. This pattern can be used to develop reliable classes by making the Design by Contract methodology—introduced by Meyer in the specific context of the Eiffel language—available in Java and possibly other object-oriented languages. Intent Provide an implementation of the Design by contract methodology [Meyer88] for developing reliable classes and create robust objects in Java. This programming pattern lets you apply assertions to guarantee the pre-conditions and post-conditions of methods and invariants on the state of objects. Also Known As Design by contract Motivation Sometimes it’s necessary to restrict a class interface because you don’t want to provide all the possible input combinations for all collaborators. One way to achieve this goal is to make minimal and consistent methods that impose pre-conditions on input parameters. Moreover, you might want to ensure the class behavior by imposing post-conditions before methods return. You might also need to make sure that the class is always in a stable state. Such an approach can result in reliable classes to create robust objects. Consider for example a bounded counter that has both a lower and upper bound.
    [Show full text]
  • Comparative Studies of Programming Languages; Course Lecture Notes
    Comparative Studies of Programming Languages, COMP6411 Lecture Notes, Revision 1.9 Joey Paquet Serguei A. Mokhov (Eds.) August 5, 2010 arXiv:1007.2123v6 [cs.PL] 4 Aug 2010 2 Preface Lecture notes for the Comparative Studies of Programming Languages course, COMP6411, taught at the Department of Computer Science and Software Engineering, Faculty of Engineering and Computer Science, Concordia University, Montreal, QC, Canada. These notes include a compiled book of primarily related articles from the Wikipedia, the Free Encyclopedia [24], as well as Comparative Programming Languages book [7] and other resources, including our own. The original notes were compiled by Dr. Paquet [14] 3 4 Contents 1 Brief History and Genealogy of Programming Languages 7 1.1 Introduction . 7 1.1.1 Subreferences . 7 1.2 History . 7 1.2.1 Pre-computer era . 7 1.2.2 Subreferences . 8 1.2.3 Early computer era . 8 1.2.4 Subreferences . 8 1.2.5 Modern/Structured programming languages . 9 1.3 References . 19 2 Programming Paradigms 21 2.1 Introduction . 21 2.2 History . 21 2.2.1 Low-level: binary, assembly . 21 2.2.2 Procedural programming . 22 2.2.3 Object-oriented programming . 23 2.2.4 Declarative programming . 27 3 Program Evaluation 33 3.1 Program analysis and translation phases . 33 3.1.1 Front end . 33 3.1.2 Back end . 34 3.2 Compilation vs. interpretation . 34 3.2.1 Compilation . 34 3.2.2 Interpretation . 36 3.2.3 Subreferences . 37 3.3 Type System . 38 3.3.1 Type checking . 38 3.4 Memory management .
    [Show full text]
  • Deferred Cancellation
    Deferred Cancellation A behavioral pattern Philipp Bachmann Institute for Medical Informatics and Biostatistics Clarastrasse 12 CH–4058 Basel, BS Switzerland [email protected] ABSTRACT General Terms People who design their own pool of worker threads [33, Design pp 290–298] or processes have to consider how to shut down the workers again or how to dynamically adapt the num- Keywords ber of workers to varying load. Especially with regard to application termination you may have the choice between an Patterns, Destructor, Actor, Reliability, Portability immediate destruction of the pool and a more graceful shut- down. The pattern proposed helps to portably implement Sir, my need is sore. such termination and load adaptation mechanisms that as- Spirits that I’ve cited My commands ignore. sume you voted for the second choice. The main area of application are the internals of active objects [40] and Johann Wolfgang von Goethe: The Sorcerer’s similar designs that delegate work to a pool of threads or Apprentice [23] processes to execute service requests asynchronously from their actual invocation. For the pattern proposed we identified usage examples in 1. INTENT popular existing applications or libraries. Safely shut down pools of worker threads [33, pp 290– Both a real world example and sample code accompany 298] or processes without resource leakages and premature the pattern presentation. This sample code is in C++. rollback of transactions. The design proposed aims at The presentation of the pattern follows the style portability. well known from [11] and [44]. This pattern is based upon other patterns. Typographic conventions for references to other patterns are similar to [3].
    [Show full text]
  • Safely Creating Correct Subclasses Without Superclass Code Clyde Dwain Ruby Iowa State University
    Iowa State University Capstones, Theses and Retrospective Theses and Dissertations Dissertations 2006 Modular subclass verification: safely creating correct subclasses without superclass code Clyde Dwain Ruby Iowa State University Follow this and additional works at: https://lib.dr.iastate.edu/rtd Part of the Computer Sciences Commons Recommended Citation Ruby, Clyde Dwain, "Modular subclass verification: safely creating correct subclasses without superclass code " (2006). Retrospective Theses and Dissertations. 1877. https://lib.dr.iastate.edu/rtd/1877 This Dissertation is brought to you for free and open access by the Iowa State University Capstones, Theses and Dissertations at Iowa State University Digital Repository. It has been accepted for inclusion in Retrospective Theses and Dissertations by an authorized administrator of Iowa State University Digital Repository. For more information, please contact [email protected]. Modular subclass verification: Safely creating correct subclasses without superclass code by Clyde Dwain Ruby A dissertation submitted to the graduate faculty in partial fulfillment of the requirements for the degree of DOCTOR OF PHILOSOPHY Major: Computer Science Program of Study Committee Gary T. Leavens, Major Professor Samik Basu Clifford Bergman Shashi K. Gadia Jonathan D. H. Smith Iowa State University Ames, Iowa 2006 Copyright © Clyde Dwain Ruby, 2006. All rights reserved. UMI Number: 3243833 Copyright 2006 by Ruby, Clyde Dwain All rights reserved. UMI Microform 3243833 Copyright 2007 by ProQuest Information and Learning Company. All rights reserved. This microform edition is protected against unauthorized copying under Title 17, United States Code. ProQuest Information and Learning Company 300 North Zeeb Road P.O. Box 1346 Ann Arbor, MI 48106-1346 ii TABLE OF CONTENTS ACKNOWLEDGEMENTS .
    [Show full text]
  • 3. Design by Contract
    3. Design by Contract Oscar Nierstrasz Design by Contract Bertrand Meyer, Touch of Class — Learning to Program Well with Objects and Contracts, Springer, 2009. 2 Bertrand Meyer is a French computer scientist who was a Professor at ETH Zürich (successor of Niklaus Wirth) from 2001-2015. He is best known as the inventor of “Design by Contract”, and as the designer of the Eiffel programming language, which provides built-in for DbC. DbC was first described in a technical report by Meyer in 1986: https://en.wikipedia.org/wiki/Design_by_contract Who’s to blame? The components fit but the system does not work. Who’s to blame? The component developer or the system integrator? 3 DbC makes clear the “contract” between a supplier (an object or “component”) and its client. When something goes wrong, the contract states whose fault it is. This simplifies both design and debugging. Why DbC? > Design by Contract —documents assumptions (what do objects expect?) —simplifies code (no special actions for failure) —aids debugging (identifies who’s to blame) 4 As we shall see, DbC improves your OO design in several ways. First, contracts make explicit the assumptions under which an object (supplier) will work correctly. Second, they simplify your code, since no special action is required when things go wrong — the exception handling framework provides the necessary tools. Third, contracts help in debugging since errors are caught earlier, when contracts are violated, not when your program crashes because of an invalid state, and it is clear where to lay the blame for the violation (i.e., in the object or its client).
    [Show full text]
  • A Machine-Checked Proof for a Translation of Event-B Machines to JML
    A Machine-Checked Proof for a Translation of Event-B Machines to JML N´estorCata~no1,Camilo Rueda2, and Tim Wahls3 1 EAFIT Unviersity [email protected] 2 Camilo Rueda Pontificia Universidad Javeriana [email protected] 3 Tim Wahls Dickinson College [email protected] Abstract. We present a machine-checked soundness proof of a trans- lation of Event-B to the Java Modeling Language (JML). The transla- tion is based on an operator EB2Jml that maps Event-B events to JML method specifications, and deterministic and non-deterministic assign- ments to JML method post-conditions. This translation has previously been implemented as the EventB2Jml tool. We adopted a taking our own medicine approach in the formalisation of our proof so that Event-B as well as JML are formalised in Event-B and the proof is discharged with the Rodin platform. Hence, for any Event-B substitution (whether an event or an assignment) and for the JML method specification obtained by applying EventB2Jml to the substitution, we prove that the semantics of the JML method specification is simulated by the semantics of the sub- stitution. Therefore, the JML specification obtained as translation from the Event-B substitution is a refinement of the substitution. Our proof includes invariants and the standard Event-B initialising event, but it does not include full machines or Event-B contexts. We assume that the semantics of JML and Event-B operate both on the same initial and final states, and we justify our assumption. Keywords: Event-B, JML, EventB2Jml, Formal Proof, Translation, Rodin 1 Introduction arXiv:1309.2339v1 [cs.SE] 9 Sep 2013 In an earlier work [14], we have proposed a design methodology that combines the use of design-by-contract techniques with JML [36,29,33] and correctness-by- construction techniques with Event-B [1].
    [Show full text]
  • Verification of Object Oriented Programs Using Class Invariants
    Verification of Object Oriented Programs Using Class Invariants Kees Huizing and Ruurd Kuiper and SOOP?? Eindhoven University of Technology, PO Box 513, 5600 MB Eindhoven, The Netherlands, [email protected], [email protected] Abstract A proof system is presented for the verification and derivation of object oriented pro- grams with as main features strong typing, dynamic binding, and inheritance. The proof system is inspired on Meyer’s system of class invariants [12] and remedies its unsound- ness, which is already recognized by Meyer. Dynamic binding is treated in a flexible way: when throughout the class hierarchy overriding methods respect the pre- and post- conditions of the overridden methods, very simple proof rules for method calls suffice; more powerful proof rules are supplied for cases where one cannot or does not want to follow this restriction. The proof system is complete relative to proofs for properties of pointers and the data domain. 1 Introduction Although formal verification is not very common in the discipline of object oriented programming, the importance of formal specification is generally ac- knowledged ([12]). With the increased interest in component based develop- ment, it becomes even more important that components are specified in an un- ambiguous manner, since users or buyers of components often have no other knowledge about a component than its specification and at the same time rely heavily on its correct functioning in their framework. The specification of a class, sometimes called contract, usually contains at least pre- and postcondi- tions for the public mehtods and a class invariant. A class invariant expresses which states of the objects of the class are consis- tent, or “legal”.
    [Show full text]
  • The Fields and the Class Invariant /** B[0..Size-1] Is a Min-Heap, I.E
    Heaps with priorities We previously studied the max-heap, which implements a bag of integers with insertion of a value and extraction (polling) of a maximum value in O(log n) time for a heap of size n. Min-heaps are similar. We now extend the idea to implement a min-heap of a set of distinct values each with a priority. The priority will be a double value. The separation of value from priority is needed in several different situations. Here are two: • Consider finding the shortest route from Ithaca, NY, to Washington, D.C. A heap will contain points along the way, and the priorities will be the shortest distances found thus far from Ithaca to those points. • In a discrete event simulation, pending events can be kept in a heap, with the priority being the time at which an event is to occur. Keeping the set in a min-heap makes it easy to extract the next event to process. We will define all the methods needed, but we won’t show many of the method bodies, since they are fairly easy to write based on the earlier discussion of max- and min-heaps. Class Heap and inner class Item /** A heap of elements of type E. */ We will implement a generic min-heap with priorities. The public class Heap<E> { start of class Heap appears to the right. An object of this class implements a heap of elements of type E. For example, E could private class Item { be a point on a map or an event to be simulated.
    [Show full text]
  • Sams Teach Yourself Visual C# 2010 in 24 Hours
    Praise for Sams Teach Yourself Visual C# 2010 in 24 Hours “The Teach Yourself in 24 Hours series of books from Sams has been a staple of anyone wanting to quickly come up-to-speed on a new technology. This book is not just a simple refresh of last year’s book, Scott has written it from the ground up for the Visual Studio 2010 and .NET 4.0 release. From the C# type system, to events and data, from ASP.NET Web to WPF Windows applications, Sams Teach Yourself Visual C# 2010 in 24 Hours will provide any developer new to the C# language a great foundation to build upon.” —Shawn Weisfeld, Microsoft Visual C# MVP “The key to learning software development is to have a great foundation. Sams Teach Yourself Visual C# 2010 in 24 Hours is a must-read for anyone who wants to learn C# from the beginning, or just brush up on its features. Scott Dorman brings a very knowledgeable, yet casual approach to his book that anyone with the desire to learn to program in .NET can be inspired by. I found a few gems that will enhance my future programming projects.” —Chris “Woody” Woodruff, Co-Host of Deep Fried Bytes Podcast “This book is an excellent resource for anyone who is learning C# for the first time, migrating from Visual Basic, or catching up on the latest features of C#. It is full of information and should be on the desks of any developer who is becoming familiar with C# 2010.” —Jeff Julian, Managing Partner, AJI Software, Founder of GeeksWithBlogs.NET “Scott Dorman has written an excellent reference book that not only covers the basic fundamentals of .NET 4.0 C# development, but also includes instruction and guidance on the finer points of advanced C# and development with Visual Studio 2010.
    [Show full text]