I've been coding primarily in .NET (VB.NET and C#) ever since it first came out, back in 2002, and on the whole, I think it's amazing. Microsoft did a whole lot right when they created the .NET framework.
I recently just took the .NET 3.5 certification exam, and studying for it, after a few years away from coding, gave me a chance to realize that MS also got a number of details wrong along the way. Allow me to share my favorites:
Much of the .NET framework was well-designed, with the poor coder who has to use it in mind. But there are occasional pieces that make me shake my head. For instance, imagine that you saw this code in someone's application: CspParameters cspParams = new CspParameters();
cspParams.KeyContainerName = "MyKeyContainer";
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(cspParams);
Unless you happened to know the CryptoAPI very well, would you realize that this is how you create and store a key-pair? In other words, creating and storing the key is a side-effect of calling the RSACryptoServiceProvider constructor, if you happen to pass a CspParams object that happens to have its KeyContainerName property set. It should have been designed to work something like this, of course: RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); rsa.Key = RSACryptoServiceProvider.CreateKey(); rsa.PersistKey("MyKeyContainer"); When you want to specify that it makes no sense for an assembly to run unless it has the permissions to open a file dialog box, you can flag that assembly with the following attribute:
[assembly: FileDialogPermission(SecurityAction.RequestMinimum)]
And that makes some reasonable sense: you're saying that it needs, at a minimum, to be able to show the file dialog box. Still, a better name would probably be something like "Require" than "RequestMinimum": it's less ambiguous.
But if you want to step your security up a notch, and tell the runtime to automatically deny all permissions other than the ones you're listing, you use the enumeration SecurityAction.RequestOptional:
[assembly: FileDialogPermission(SecurityAction.RequestOptional)]
To call required permissions "optional" makes absolutely no sense, and it makes it significantly more difficult for your average coder to figure out what's going on. It would be much easier to understand if MS had chosen to call it something like "RefuseAllExcept":
[assembly: FileDialogPermission(SecurityAction.RefuseAllExcept)]
(For what it's worth, it's called "Optional" because whoever came up with the name was thinking from the perspective of the runtime: "Do I need to throw an exception if the assembly doesn't have this permission? No? OK, then it's an optional permission." But you don't flag an assembly with this attribute because you're concerned to communicate that it might not need this permission: you add this attribute because you only get the permission you need, i.e., you want to make sure that the assembly is good and secure and only does things you're explicitly aware of, and that the runtime actively refuses to let it do anything you hadn't previously thought about.)
Certain methods and classes in the .NET framework typically take parameters in one order; but other methods and classes take them in a very different order. There are numerous examples of this, but to take just one: if you want to format a string according to a specific culture using the String.Format() method, you pass parameters in the order "CultureInfo", "FormatString".
String.Format(new
CultureInfo("en-UK"), "c", 100)
But if you want to use the ToString() method of an object to format it, you pass them in the order "FormatString", "CultureInfo".
d.ToString("c", new
CultureInfo("en-UK"))
Stupid things like that just make a framework harder to use than it needs to be: it's one more thing to remember in a world where I've already got too much going on.
In general, there are two different ways you can get an instance of a class: you can do it yourself, or you can get something else to do it for you. Either one works fine, though there are reasonably good arguments for preferring the second (known as a "Factory Pattern"). The problem is that the .NET framework is inconsistent throughout. Some classes can only be instantiated with their own constructor:
AppDomainSetup setup = new
AppDomainSetup();
Other closely related classes can only be instantiated through a static factory method:
AppDomain domain = AppDomain.CreateDomain("My App Domain");
And others can be instantiated either way:
Process process1 = new Process();
Process process2 = Process.Start("cmd.exe");
Frankly, I probably wouldn't care that much: it's mostly a stylistic difference, and the MS documentation is handy and usually pretty good. But any number of questions in the 70-356 exam turn on knowing whether you use a static or an instance constructor, and remembering that you can only get an AppDomain object by calling a static constructor, but you can get a Process object either way, gets old after a while.
I've been using VB since version 1.0, long before it was a commercially viable language, and long before it had anything like object-oriented features. VB.NET is arguably a completely different language, but one of the things that remains very much the same, and very easy to do, is the handling of events. The syntax for declaring them is simple and intuitive, and easy enough for nearly anyone to understand. To handle an event in the most basic fashion, you have to specify four different "elements": you declare (1) the event and (2) the event handler, (3) you connect them by instantiating the event class using "WithEvents", and then (4) you raise the event.
Class AlarmClock
Public Event Ring() Sub MakeMeRing()
RaiseEvent Ring()
End Sub End Class Class Sleeper Public WithEvents Alarm As New AlarmClock Sub WakeUp() Handles Alarm.Ring
Console.WriteLine("I'm awake, already.")
End Sub End Class In contrast, in C#, for the most basic event, you have to follow seven steps. You declare (1) the event (2) the event handler, and (3) instantiate the class holding the event. You then wire up the event by (4) instantiating the delegate, and (5) adding it to the event. You then raise the event by (6) checking if there are any handlers, and then finally (7) you raise the event.
class AlarmClock
{
public event EventHandler Ring;
public void MakeMeRing()
{
if (Ring != null)
{
Ring(this, new EventArgs());
}
}
} class Sleeper
{
public AlarmClock alarm = new AlarmClock(); public Sleeper()
{
alarm.Ring += new EventHandler(alarm_Ring);
} void alarm_Ring(object sender, EventArgs e)
{
Console.WriteLine("I'm awake, already.");
}
} For only one of those steps (declaring the event) does the language itself provide any structure for you: you otherwise have to use other unrelated language elements, such as delegates, constructors, and operator overrides: and you have to understand each of these, and think about each of these, before you can raise and handle events. In contrast, with VB, you only have to think about events, and not about three or four other unrelated language elements.
I'll grant that the C# implementation is elegant and flexible: but if you want, you can do it the same way in VB. VB just provides an alternate, much simpler way of handling simple events. The C# team has implemented plenty of cool features like LINQ, anonymous methods, generics, extension methods, nullable value types, and all sorts of other cool things. It's a shame that they haven't figured out a simpler way of raising and handling events.
ents.
I've got more gripes – of course J – but the rest will have to wait.
Bizarre Side-effects
Bizarre Names
Screwy Parameter Orders
Inconsistent Object Instantiation
Event Handling in C#
Infant Baptism and Church History
5 hours ago

|