June 2005 - Posts
A very thorough review of Effective C#Jim Holmes, who runs the Dayton Ohio .NET User group posted a very thorough review of Effective C# yesterday. (Thank you, Jim).
The slashdot ReviewMore than 150 comments, and growing
Jim's follow-up commentsAfter seeing the response on SlashDot
The Dayton Ohio .NET User GroupYou know, if you're in the Dayton area.
Fawcette Technical Publications is sponsoring discountsFTP has been working with several publishers (blantant plug) including Addison-Wesley, to help you fill up your bookshelf with more great titles (another blatant plug), including Effective C#.
Check it out. Buy a book or two and improve your skilss.
FTP Online bookstoreThe landing page
.NET ObfusactorI promise to post links very sparingly, but this is worthwhile knowledge for any professional developer.
If you use Reflector (and who doesn't) you've likely wondered how to protect your code from other developers peering eyes.
This blog discusses obfuscation in depth. Yes, it's written by the people at Preemptive (see links), but it's not a commercial. (If it was, I wouldn't bring it to your attention). It's information about how obfuscation works, it's impact (positive and negative) on performance, and other related issues.
Trust me, it's technical meat that you need to know.
.NET ObfuscatorFrom the guys at Preemptive
The Reflector Home pageIf you don't know what I'm talking about
Preemptive home page.The guys that build dotfuscator
Anyway you slice it, it depends on the specific uses in your application.Here is yet another question on smart client architecture:
My application works as follows:
The desktop application will download the database from the server (when it installed in the user machine). User will not do any changes to the local copy of the database. However, the application will link into a web service (say once a month) to obtain a new copy of the database.
I can reduce the network traffic, if I can retrieve only the changed/new records from the database in the server.
Ok... I send a copy of my local data as a dataset to the web server. Is it possible to use this database to check for changes in the server database and to get only the changed/new rows from the server database? Then I can update the desktop copy with new rows.
And the answer is:
… complicated.
There are three variables that you are working with, and you want to minimize all three:
First, how often your program must communicate from client to server.
Second, how much data must be retrieved when your program communicates from client to server.
Third, the extra logic required to handle out-of-date data at the desktop.
To begin with, a Dataset is just the wrong class for the scenario you’ve outlined above. Tracking changes in a Dataset object will work to update a database at the server with changes from the client. But, going the other way is just not particularly effective. You’d need to have a different dataset object that tracks the differences between the current database contents and the contents as last delivered to each and every desktop workstation. That simply won’t scale, and adds quite a bit of extra processing on the server. Don’t do that.
You suggested sending the entire database copy to the server, and then sending a new copy back down to the client. That will seriously hurt the second point above (how much data should be transferred between the server and the client.)
So, what to do? Well, to some extent, it greatly depends on your application. However, there are some general ideas that you can use to pick the best solution. First, consider the best granularity for updates. A good first pass solution often is to send any and all tables that have changed to the client.
Decide if records will be deleted in your application. (Many scientific applications will mark data as deleted, but not permanently remove it. It provides a better audit trail). Deleted records are often handles in two different ways. You can either completely replace the table at the client side, or write methods that send records marked for deletion to the client. The client can then delete those records. Which you choose depends on both the size of the table, and the frequency of delete operations.
Some final recommendations
This question is just too broad for a blog post. The fact is that the best way to manage data synchronization between client and server will be very application-specific. You’ll need to analyze the user cases for your application, and optimize your design for the most likely scenarios. But, in all cases, the dataset transaction mechanism is not the best method for sending changes from the server to the client.
There's no hidden meaning, really.
A friend asked me why there is a violin on the cover of Effective C# instead
of any other instrument.

The logic was actually pretty simple. A musical instrument seemed
logical for a C# book. At the time, four of the five members of my family played
some instrument. I play guitar, my wife plays piano, Lara plays flute, and Sarah
plays violin. (Scott has started playing the trumpet now, but that predates the
book).
The piano makes a silly cover. The guitar seemed wrong, and a flute on the
book cover looked a bit silly. So violin won by default.
Sort of like software design: Throw out all the lousy ideas, and the
one that's left is probably pretty good. At least I hope so.
No, they are not supported, but you can achieve the same functionality using generic interfacesOne of the attendees at VS Developer Connections yesterday asked about covariant return types in C# during the “Ask the Experts” panel. I said they were not supported, but I’d look it up again to verify. I did remember correctly: C# (all versions to date) does not support covariant return types.
Backing up just a bit, let me explain covariant return types. The following is legal C++ (which does support covariant return types):
class B
{
public:
virtual B ProduceObject()
{
return new B();
}
};
class D: B
{
public:
virtual D ProduceObject()
{
return new D();
}
}
Notice that the return type of D::ProduceObject does not match the return type of B::ProduceObject. It is a valid derived class, but it is not the same type.
A similar construct in C#, would not compile:
public class B
{
public virtual B getThing()
{
return null;
}
}
public class D : B
{
public override D getThing() // Illegal C#
{
return null;
}
}
But there is a way to get a similar affect. You can use generic interfaces to define the functionality you want, and a specialization of the interface implementation can provide the proper function:
public interface Producer<T>
{
T getThing();
}
public class D : Producer <D>
{
public D getThing()
{
return null;
}
}
No, this does not solve every use case for covariant types, and there are some differences. The example I showed above places no restrictions on the type that implements the producer interface. That’s very broad for most uses of covariant return types. However, by extending the Producer interface with the other functionality you desire, or by adding constraints on T, you could get the same functionality that you achieve with covariant types in C++ (or other languages).
My favorite quote from VS2005 Dev Con in DetroitIn response to questions about C#, and the 'My' namespace, David White (MS Architect) made the comment that VB has always been about productivity and C# and C++ have always been about pain.
In response, Stacy Harris (MS Architectural Consultant) gave that response. The entire crowd laughed (no matter what language they preferred).
From an older article that is still online, comparing my approach to another author's.Yet another reader question (from an older article online at fawcette.com)
I just read your VS Magazine article (see links below) regarding data-driven client validation and I had sort of a general question for you. How does your approach differ from the approach taken by Enrico Sabbadin in the March 2004 issue of VSM (see links below)? Is his technique one of the data distribution methods that you outlined in Table 1 of your article? Do you have an opinion on his techniques? I am looking at this from the perspective of how best to implement business objects within our enterprise. Any advice you can give would be highly appreciated.
Enrico’s algorithms are a form of the validation we proposed in the first line of the table in our article. (I want to remind everyone that the article referenced was co-authored by Tony Surma, whose insight was invaluable. If memory serves correctly, he provided all the information in the table referenced.) Enrico’s solution used code to handle the validation events at the client side. He used the same events that we did, but his validators were using rules that were baked into the code. We tried to provide generic code for classes of rules that would be common in business applications. Then, a controller would read an XML configuration file (using any of the methods shown in our article) and create the proper objects using the data defined in the objects.
The main difference between Enrico’s approach and ours was that we built in some capabilities to modify the business rules by modifying the data file that drove them. This extra capability comes with increased complexity. So I would recommend our solutions only when it’s expected that such changes are likely in the future.
The article by Tony Surma and meBuild Data-Driven Client Validation
The article by Enrico SabbadinUse DataSets as Business Objects
An Effective C# reader had a number of questions on Exceptions and RemotingThrowing Exceptions across appdomain boundaries is more complicated than it is in other situations.
The full discussionIt's in my Effective C# blog, because it centers on book items
The short version: It doesn't matter much, so write clear code, then optimize if necessary.I received the following question about how the C# compiler and the JIT compiler optimize for loops:
Item 11 says that the for loop with the length variable hoisted out of the loop is the worst option as the array index is checked in every iteration of the loop. However when we looked into the IL code for the two examples given in the book, the IL instructions were found to be similar
Example 1:
// Loop 2
for ( int index = 0; index < foo.length; index ++)
Console.Writeline( foo[ index ].ToString());
IL Expansion snippet:
L_0019: ldloc.s numArray1
L_001b: ldloc.s num2
L_001d: ldelema int32
Example 2:
// Loop 3
int len = foo.Length;
for ( int index = 0; index < len; index ++)
Console.Writeline( foo[ index ].ToString());
IL Expansion snippet:
L_001b: ldloc.3
L_001c: ldloc.s num2
L_001e: ldelema int32
( For the second example object reference to the array is stored at index 3 of the local variable list. )
The ldelema opcode pops the index and array from the stack; the address stored at position index in array is looked up and the address is pushed onto the stack. An IndexOutOfRangeException is thrown if index is negative, or larger than the bound of array.
Now this opcode is present for both the loops, hence execution should be identical. I am a little confused, do let me know if I have missed any thing here.
The answer is in an instruction you left out in your snippet. The ldlen instruction pushes the number of elements of a zero-based, one dimensional array on the stack. The JIT compiler can make some assumptions about the code because of that instruction, and the fact that arrays cannot change size.
First, the loop is bound by the size of the array. If you look at the loops, the ldlen instruction is inside the loop on the first construct, outside on the second. The JIT compiler can recognize that the loop variable is tested directly against the array size, rather than against some local storage var that may or may not have the same value as the array size. That facilitates the optimization.
Of course, for the best answer, you should profile it. Here’s my results:
Method 1, Average over 20 Passes, Elapsed Time: 0.243583441124793
Method 2, Average over 20 Passes, Elapsed Time: 0.251449644520313
Notice that the difference is slight, but it is there.
online at fawcette.comThis article explains a few simple extensions you can make to the VS2003 windows datagrid that greatly improves its usability. While you might not use these exact extensions, they will show you some general idioms that will help you create more robust data view UIs.
The article5 Easy Windows Datagrid Extensions