
Journal Bill Dog's Journal: big C# disappointment 5
A project I'm working on has a bunch of methods that take an ADO.NET DataRow that use the square bracket/array accessor notation on it to get at the actual value for that particular column (of that row (from the database)).
I needed to add versions of these to instead take an ADO.NET SqlDataReader. In doing so I noticed that it uses the same array accessor notation, so the bodies of these functions turn out to be identical to their DataRow counterparts.
To refactor away these duplicates, I would've thought to make them take a common base class that defines the indexer (for that array accessor notation), but I already know that they don't have a common base class (well, besides Object). (And this is perfectly understandable, really, because they represent two entirely differently working data access models.)
So my thought was to make these, in C++ parlance, function templates. C# has generics including generic methods, so I try.
End result: No can do.
The MSDN documentation dodges this obviously desirable capability by only giving sample code using the generic type T as a black box, in a swap function. No sample showing it actually calling a method on T.
It looks like you can do this if T will always have a base class B in common, that has the called method defined, and then if you use the optional add'l constraint syntax to instruct the compiler of this. That's worth something, but is missing the really powerful benefit.
Dynamically typed languages like JavaScript (and Smalltalk from my long ago readings of the GoF book) let you pass any object into a function as a parameter and if that function wants to call a method on that object, it waits until run-time to see if the actual object that is passed in has that method.
Statically typed languages like C/C++/C#/Java need to be able to verify at compile-time that that method will be there, for any object that might possibly be passed in as a parameter. The C++ compiler figures out what objects you're calling that generic function with, and auto- stamps out for you a version of it for each such one that's actually being passed in. Thereby satisfying its own requirement, as a statically typed language. [Please forward any complaints about this "bloat"ing out the code to the Windows recycle bin.]
What the C# compiler does is generate only a single copy of the function, with simple placeholders to be filled in later. That's why you can't use the generic argument in any ways that you can't syntactically spoon-feed assumptions about it to the compiler. And it won't compile if it cannot make sufficient assumptions about it.
This is fine for for example generic container classes. Like in C++ how std::vector doesn't need to know anything about the type it holds, neither does System.Collections.Generic.List<T>, because List's functions never call methods on the T or anything like that. That's great, that I can declare a strongly typed container that won't take anything but a certain type (or descendants), rather than always just a container of Object that requires a bunch of casting and if you mess up can readily accept objects of different, unrelated types. But generic programing is capable of so much more.
<sidebar>
I've heard that Java's generic handling still has the container taking Object, but just does all the casting for you. I.e. C++ > C# > Java.
</sidebar>
So in this case there's no difference between C++ and C# in how many functions are generated in the end (there's code that uses them all), the difference is that the other half of them *I* had to generate. Lame.
p.s. It seems with this new JE interface you can no longer make an edit after preview and expect an immediately following preview to show the update yet. Even lamer. How come it's always amateur hour in the dev area of the Slashdot office? Cuz they only hire FOSSies? What if MS Word didn't show the latest version of the document in its print preview, unless you gave it a minute or three and then tried the print preview again?
MS isn't better than FOSS, it's just polished (Score:2)
How come it's always amateur hour in the dev area of the Slashdot office? Cuz they only hire FOSSies? What if MS Word didn't show the latest version of the document in its print preview, unless you gave it a minute or three and then tried the print preview again?
There is nothing inherently wrong with "FOSSies"... If Microsoft doesn't release the code, then it's even worse quality than anything I've seen in open source. Only the stuff that they ship gets a polish. (Note: I said nothing about the content being sound, just that it gets polished.)
Frankly, I was highly disappointed at the vast majority of code that I came across at Microsoft.
Interfaces (Score:2)
I think what you're looking for are not generics, but interfaces like they are implemented in Google Go!. (http://golang.org/doc/go_faq.html#implements_interface)
In go when you define a interface to have a certain method/signature any existing class which happens to have those method signatures are automatically considerd to be implementing those interfaces.
So in GO, you would define a interface which has the indexer method, and make your functions/classes accept that interface rather than a base class, and
Re: (Score:1)
Thank you very much for that link. "Duck typing" was mentioned there, and when I Googled that plus "C#" I saw the new "dynamic" keyword in C# 4.0. I had got a whiff of that word before, but never got around to investigating what it meant nor have ever seen it in practice in code as of yet.
Documenting this here for myself and in case you're interested, I had to eschew generics as you suggested, and replace Foo<T>(T t) with Foo(dynamic t). This gives a compiler error message that suggests a missing refe
Here's what I'd do (Score:2)
But it does seem needlessly complex:
Overload the original Datarow function with one that takes a DataReader, converts it's values to a data row, then calls the original function.
Note: Only works if you are working read-only to begin with. If you need to output back to the datarow, then the overloaded wrapper function will be twice as complex- because when you get the datarow back from the function, you then need to edit/update the data reader.
Re: (Score:1)
That's what I was doing, adding an overloading function taking a DataReader for every function that took a DataRow.
Yes, it would normally be best to employ forwarding from one set of them to the other, rather than duplicate their bodies. A good software engineering suggestion. In my case, they're only one-liners, so I haven't done that. But I might decide to. (That is,