C#88: The Original C# (2018)
medium.com
medium.com
https://github.com/dotnet/coreclr/blob/release/1.0.0/tests/s...
There was also a great deal of confusion around the way Microsoft originally introduced COM. When Windows 3.1 came out, one of its marquee features was something called OLE 1.0 - Object Linking and Embedding. This was a composite document approach built on an early technology called DDE, Dynamic Data Exchange. (Which was brittle, and based on Windows messaging.) A few years later, they introduced OLE 2.0, which added a bunch of features, but mainly completely replaced DDE with COM. For a while, there was a lot of conflation of COM as something OLE 2.0 specific.
And this must be related to needing to have pdb file or being unable to debug.
I wish those two things were not the case. And nuget.
I find C# wonderful except those particular decisions give me pain on a daily basis.
I agree that having lots of projects is a pain in the ass, and projects should be used for organizing code into deployment units not for logical separation. But this isn't a feature of the language just a decision(I believe a poor one) a previous developer on your project made.
If you need to share a module between teams, it’s easy to package it up as a versioned Nuget package. If you need to separate it out into its own http based microservice. It’s easy to put a Controller on top of it and create an auto generated proxy class if you are describing the API with Swagger.
On the client side, have the proxy class implement the interface.
Of course all this is made easier if you are using a DI framework.
[1] again I’m not referring to starting out with out of process microservices. They can very well be logically separated within a monolith and using class access modifiers as appropriate.
[2] if you have clean namespace boundaries within your monolith, you program against interfaces instead of classes, and you use a DI framework. Pulling out an assembly/“service” from a monolith becomes a mechanical exercise.
Having debug symbols in the binaries also seems like an odd choice to do (at least by default) as they can eassily be 10x the size of the binary.
(b) Debug symbols have to be there for you to be able to have source-level debugging, of course. You can always debug the assembly code, of course. For Microsoft's tooling, PDB files are the debug symbols that link the assembly code to the source code. Other tools sometimes embed debugging information in the binaries. And if this information isn't there, you can't have source debugging either.
(c) No one's forcing you to use NuGet, although I wonder what you particular problems are with it.
(b) debug symbols could just be included in the compilation unit and optionally stripped out using a flag when you compile. There isn't a need for it to be in a separate file. A lot of fun times attaching DnSpy to something to half-ass debug it through a decompiler because there was no .pdb and you can't debug without it. Just painful. Wish they never split the debug symbols into a separate file.
(c) I guess my colleagues are forcing me to use Nuget as I can't just show up tomorrow and switch everything to Paket. I don't have to use it on my personal projects, which is nice.
Nuget's problems stem from what I'm talking about here. You need Newtonsoft.Json in various different csproj files, so you reference it in both of them. If you're not careful it easily winds up in a situation where you have multiple different versions of .NET in a single .sln file and multiple different versions of Newtonsoft.Json and the dreaded "multiple conflicting versions of a dependent assembly" warning. Etc.
Comparing it to my experience in the Java world I remember it's still possible to split the project up and open separate parts if I want, but the whole thing has one unified version of Java and a unified set of dependencies. Just never wound up with the messy tangles I see in .NET projects.
It's about my only beef with .NET stuff. Really irks me. Paket looks like a step forward though in terms of Package management, but it's not the defacto standard.
embedding debug symbols into assembly would be sometimes problematic, especially when you have public and private symbols -- signing three versions of the same assembly is not really a good idea.
(a) (c) I can feel you.. But here's what I've done to this problem:
1. use cmake for the source tree. this way you don't have trouble opening the tree as a whole. 2. in visual studio you can have both the cmake view and a solution view -- you can open a solution right from the cmake view or switch to another. 3. in this way, a solution becomes a smaller unit to organize multiple projects together. in case two projects do not have strong coupling, separate them in different solutions, and use a local nuget package source to reference. 4. of course, you'll have to roll some custom cmake script for managing nuget packages. I've done some work on this, here: https://github.com/Microsoft/GraphEngine/blob/master/cmake/F...
I've been using this approach in a few of projects at work(and also OSS ones). Insofar people are happy with it.
For personal projects I would never ever use java just because the amount of friction around builds and packaging is so terrible.
As a counterpoint, I do tend to use the JVM for personal projects, but I'm also pretty strict about keeping complexity out of the build and deploy process. What that means, practically speaking, is that I compile to uberjars, and deploy as Unix services. I also have a little library that makes it easier to use an embedded in-process database for persistence.
There are surely limitations to this approach, but I don't have enough time for my side projects to get to the point where I ever hit them. So I wind up in a spot where I can generally focus on whatever the small goal of the project is.
I personally use Maven over sbt for Scala projects though, I much prefer declarative builds to... whatever leaky DSL sbt is rocking.
May I ask why you're routinely shading? I only really use shading for deploying Spark jobs onto a cluster I don't control. Otherwise if I'm deploying an executable jar the assembly plugin and jar-with-dependencies does it fine.
Have you seen Capsule? http://www.capsule.io
You're quite right that by default there is no hand holding by Java itself on the matter and if you have to solve it yourself it's quite a not fun problem the first time you come across it.
It bothered me in the early days, but after switching to Spring Boot the problem melted away entirely. If you use https://start.spring.io select what you want, hit download and import as a Maven (or Gradle) project and bobs your uncle. Boot automatically builds a fat jar and it just works. Friction drops to zero in that particular configuration.
I'm not a fan of Maven either. Before leaving Java land I settled on using Gradle and was much happier with that. Some people don't like that it runs a daemon and uses resources or that builds are custom with it, but I don't mind those things at all and I found it a real breath of fresh air compared to Maven.
Another point of difference between native .NET shops and native Java shops that I have found is approach towards development environments. My sample size is small here, but so far I've seen that Java shops run windows, but have development VMs that you can just download an image, drop it into your machine, pull down the repo and the image is pre-configured with all the right stuff to build and develop. Linux natives are heavily into scripting things and the devops usually works quite nice, but I find it also breaks more frequently because it tends to be _somewhat_ brittle.
In .NET shops I haven't seen that. It's mostly been they run on Windows and develop on Windows (duh) but that means you have to manually do a bunch of setup and spend a day installing stuff when you start and everyones machine is a tiny bit different.
I can't say I've found one better than the other, but if it were me I'd run .NET Core development and do it on Linux in VMs.
It has nothing to do with the language. You can organize your code however you want, but overall it's a far better setup than many other languages that are folder or path based instead.
IMO when you see a lot of variance that's a smell. There isn't an obvious or known good way to do something or there is no convention you get a lot of entropy creeping in. I find the folder/path based stuff at least has a convention or two that keep things in roughly the same shape from project to project. I prefer that this is the case than what I see with C# projects.
Anyway, it's just my pet peeve.
But the point is that it has nothing to do with the language.
This has been extremely handy for us as we’ve been slowly upgrading to .net core. Because we can literally do it one project at a time without even risking breaking anything. Before going to .Net core it made switching from Web Forns to MVC really, really painless because all the business logic lived in its own projects, allowing us to run and maintain the Web Forms project, while building its replacement within the same solution.
I guess it requires you to think about the architecture of your solution, but what stack doesn’t? I say “is/was” an advantage though, because it’s primary use cases are frankly mostly related to the past or really bad practices. It allows you to keep a single project running on some really old .Net version, to utilise something legacy. That’s bad, but sometimes it has real works value, even if it’s bad.
NUGET has been fairly terrible though, and despite its many improvements, frankly continues to be so.
Ironically consolidating your packages cross projects while using NUGET is suggested by standard, but I’ve already shared my views on NUGET being terrible.
Anyway, my point goes something like, if you’re not utilising OOP “correctly” in C#, then why would you be using OOP “correctly” in a language that is even less opinionated about isolation than C#?
I say “correctly” in quotes because I personally like a high amount of isolation in my OOP, but I’m sure not every one does.
I mean, there's nothing to prevent you from spreading out namespaces across multiple assemblies. You can specify namespace and assembly in the "properties" dialog for your project, and also in within the source for each cs file, etc. I've never understood this lack of "guard-rails" in the tooling to prevent these kinds of messes. It seems like that would be reasonable thing to expect?
My opinion today is that it's easier to have more understandable code than apply "Enterprise" patterns early on. Create classes and test from them instead of deep DI/IoC patterns and interfaces everywhere. In the end, build it like it's throw away code and have high code coverage requirements when in doubt.
I do understand the various patterns, and have worked with them, and more often than not, the path leads to a big mess in practice. I tend to lean away from smarter classes and instead favor POCO + Utility Classes with Single Instances. YMMV of course.
You can write C# with far less bloat, and compile it using only the command line, but Visual Studio generates the usual project layout by default.
By the way, .XBF (XAML Binary Format, used in UWP apps) has a similar skeleton. So the heritage of C#88 lives on.
P-Code? As in UCSD Pascal?
What was happening, however, was that part of the compilation process was built into the editor and run incrementally. When you typed in and completed a line of code, it would be shipped off to the compiler, analyzed for errors, and then represented in the editor. This was easiest to see in the fact that the editor would automatically capitalize keywords, but also visible in the fact it would immediately report certain kinds of errors and make other minor code corrections. Heady stuff in 1988 when running at 4.77MHz. (The same was true for the interactive debugging tools built into the IDE.)
In any event, this was the precursor for similar functionality in later products, ranging from MS BASIC 7.0 to the Visual BASIC series.
BASIC 7.0 was also interesting in that it was at the tail end of Microsoft's BASCOM product line for DOS. For years, they'd offered a BASIC compiler product in parallel with the interpreted products that got most of the press ant attention. The compiler product was what you used if your interpreted BASIC program needed more execution speed or better packaging. It was BASIC 7 where the QuickBASIC IDE officially converged with the full machine code compiler. BASIC 7 included the IDE, a full compiler that could also target OS/2, and an ISAM database library. It was really a precursor to all the uses of VB to build line of business apps, etc.
> I was told that many office applications (on windows 3.1) were 90% pcode and 10% assembly.
The C compiler (at least) had options for compiling C into pCode that would be interpreted at runtime. I've forgotten most of the details other than that it was mainly sold as a code compression scheme of sorts. This was on the observation that pCode was more compact, but slower. (The pCode and the interpreter itself were all bundled into an x86-specific binary file, so there was no pretense of any of the cross-platform aspirations commonly associated with pCode.)
For speed, if you had a “subroutine” (gosub/return) that was called frequently, it was better to place it at the beginning of the program because it was an O(n) operation to travel the linked list.
It makes me sad thinking about how much more I knew about the underpinnings of my computer architecture and chosen language in the 8th grade than I know now as a working professional. I can still write 65C02 assembly language using an Apple // emulator but I wouldn’t know where to start with x86.
You may well be right... it's been a longer time than I care to admit.
> I can still write 65C02 assembly language using an Apple // emulator but I wouldn’t know where to start with x86.
My job out of school was at least partially to write software for an embedded 4MHz 80188... not too different from an original PC. Even in such a limited environment, virtually everything was written in C. This includes things like interrupt handlers directly invoked by the CPU and task switching code in our primitive RTOS. I think the only assembler was a bit of startup code.
> It makes me sad thinking about how much more I knew about the underpinnings of my computer architecture...
I see your point, but my view is that the goal of many of the abstractions we have is to make it possible to shift our focus to higher level and presumably more important concerns. It doesn't always work out that way, of course - sometimes the abstractions get in the way - but I do miss the power of today's languages when I go back to lower level tooling.
One side note to this is that the embedded project above started out running in real mode X86, but by the time I'd left, we had ports for 32-bit protected mode, MC68K, and a version hosted Win32. There were sound technical and commercial reasons for all of this, but it all would have been a lot more costly to achieve in time and money if we'd been coding in assembler. In other words, we arguably gained by not knowing more about the underpinnings of our computer.
WAIT 6502,0(Yes, you can wait for a value in any of 65,535 memory locations... but not this one, because we needed an easter egg...)
https://www.c64-wiki.com/wiki/Microsoft#Easter_Egg_.28Micros...
C#88 inspired thoughts but its code basically died in 88.
This is extremely interesting per se and I wonder why it didn't catch up. Is there anything similar available today? It would be interesting to know more about the original syntax of C#88.
The complete lack of any other information is suspicious.
> Microsoft first used the name C# in 1988 for a variant of the C language designed for incremental compilation. That project was not completed but the name lives on.
Super fun stuff!
What was that like? What parts did you work on?
(And thank you... I cut my teeth on Windows 3 in particular and learned a lot, I am sure, from code you helped contribute to.)