The convenience of .NET

Richard Lander

Convenient options are available for almost every task in life, from getting a ride to the airport to writing code. Convenience is the idea that a great solution is available when you want it and that it works for you. As designers of the .NET platform, we aim to provide convenient solutions for many tasks and to improve the convenience of writing apps with each new release.

This post kicks off a new series, exploring convenient solutions to common tasks. Productivity, performance, security, and reliability are hallmark design points of the .NET platform. We described them in detail in our recent Why .NET? post. Stephen Toub also published his annual performance post, Performance Improvements in .NET 8. This post (and the ones that will follow) explores the ideas and features discussed in those other posts in terms of convenient solutions. You’ll see a combination of high-level utility APIs that offer a nice balance of those design points and lower-level APIs that enable you to achieve a different balance per your needs.

The next posts go into much more detail on specific API families, with a lot of code and performance numbers, to fully explore these convenient solutions.

Let’s start the series with a more general exploration of how the .NET platform delivers on convenience.

Convenience is a spectrum

I like using the terms “convenience” and “control”, to describe the two ends of the “convenience spectrum”. Convenience is descriptive of the experience of writing code and control of your ability to define its behavior.

The most convenient code is compact and straightforward, often with at most a few options to vary behavior (as “choice” is itself a complexity). File.ReadAllText() is a good example. It returns the contents of a (text) file as a string that you can read and process. The lowest-level code enables a lot of flexibility, control, and performance optimization, but requires more careful use. Looking at you, File.OpenHandle() and RandomAccess.Read(), which together expose an operating system handle with very little getting in your way to read through a file (as bytes) with maximum performance.

I’m going to show you a couple lists of APIs. It’s OK if they don’t look familiar. These APIs start with the most raw concepts and formats (the highest degree of control) and end with the most packaged and refined concepts (the most convenient).

Convenience spectrum for reading a text file:

  • Most control: File.OpenHandle + RandomAccess.Read
  • More convenient: File.Open + FileStream.Read
  • Even more convenient: File.OpenText + StreamReader.ReadLine
  • Even more convenient: File.ReadLines + IEnumerable<string>
  • Even more convenient: File.ReadAllLines + string[]
  • Most convenience: File.ReadAllText + string

Convenience spectrum for reading JSON text:

  • Most control: Utf8JsonReader + Pipelines or Stream
  • More convenient: JsonDocument + Stream
  • Even more convenient: JsonSerializer + Stream
  • Most convenient: JsonSerializer + string

Note: The APIs are listed as the primary API + their most likely companion API or type.

A key takeaway is that there is no clear break between convenient and control patterns in these lists. The end of one convenience pattern overlaps with the start of the next control pattern. One person’s convenience is another’s control. That’s the definition of a spectrum.

Convenience starts with choice

You might wonder why we need all these APIs. They all do the same thing, right? The first is that each of these options is the right tool for the job in different circumstances and is convenient for that circumstance. In fact, the .NET developer community consistently requests a broad sprectrum of APIs from us and we’re happy to deliver them. The second is that we had to build the low-level APIs in order to make the high-level ones. It’s a lot like towers of lego blocks. In theory, we could have exposed only the high-level APIs by making all the low-level ones private, but that’s neither desirable nor practical in the general case.

Some developer stacks primarily expose high-level APIs that are built on native code libraries, but are missing useful lower-level APIs. The native code libraries are often written in a way that makes it impractical to expose the lower-level APIs to a managed language, so they are not. That’s quite limiting. With .NET, we have a strong philosophy that the library functionality we build should be written in C#, which means that both high- and low-level APIs are available for you to use. It also means you can read the code of all the APIs you use in C# (on GitHub), like the File class.

Of course, there are places where we haven’t exposed all of the layers; every new API we expose is something we’ll have effectively forever, and requires design and direct testing and maintenance and documentation and compatibility constraints and so on. We’re thus selective in which layers we expose when, and are constantly re-evaluating whether additional support should be exposed. The previously mentioned File.ReadAllText, for example, has been around for many, many years, whereas the cited RandomAccess.Read was only recently introduced. Our long-term trend has been to make lower-level APIs available where there is a compelling case.

Convenience enables collaboration

The .NET libraries exposes a broad set of functionality for you to use. In many cases (like with the File type), much of the related functionality is exposed in one place and designed to work as a larger coherent system. That means you can use more convenient APIs in one part of your code and higher-control APIs elsewhere and it can all be made to work together, nicely.

“Hey … I’m going to be writing this data to a Stream with APIs that give me the control we need for our service. You can use StreamReader.ReadLineAsync to read it. If that doesn’t work, I’ll expose an IAsyncEnumerable<string> for each line and you can use await foreach as a streaming solution. Either option works for me. I love how straightforward all of these options are. It is super easy to connect our code together and it’s all super fast and convenient.” — .NET dev at ACME Solutions.

Developers working in teams can make different (and equally good) convenience choices at different layers within a larger codebase, with straightforward patterns to connect those layers.

I’m in control

Yes, yes. The point here isn’t to pick a point on this spectrum and stick to it for all the code you write. Instead, the intent is to select APIs that satify the requirements of the algorithm at hand, even if you have the skills to write more challenging code that may be better on some metric (which may or may not matter). The person who maintains your code next might not have your same skills and may (incorrectly) conclude the pattern you chose is a requirement when its not.

We use convenient APIs in some places in .NET libraries, even though they are not the maximum speed. They makes the code small, simple and easy to understand and that can be more valuable than maximum speed.

That’s what one of our architects had to say about our approach to our codebase, even in a team dedicated to high performance. We like to write convenient code whenever we can. We’d rather focus our efforts on building more features and optimizing APIs that are likely to get called in a hot loop.

The other side of the coin is that the more efficient the convenience APIs are, the more we’ll be able to use them without concern in our codebase. It makes the team as a whole more efficient. We try to make convenience APIs as efficient as possible within the confines of what the shape of the API allows.

Breaking the spectrum

There are a few cases where a single API covers the majority of use cases. This only happens when an API with a simple contract is an absolute workhorse and is required by a lot of scenarios.

The string class APIs are a key example. IndexOf and IndexOfAny are two favorites. We use these APIs pervasively in the .NET platform and they are used just as much by .NET developers. You can see how many PRs have targeted those APIs.

Many of the IndexOf{Any} calls are actually on spans now, rather than direct calls to string.IndexOf{Any}. While the spans are frequently pointing into strings, these APIs often operate on slices (after calling string.AsSpan, internally).

This family of APIs have been improved a lot, using multiple techniques to improve performance. For example, these APIs uses vector CPU instructions to search for search terms in a string. In .NET 8, support for AVX512 was added. That’s not yet relevant for most hardware, however it means that IndexOf will be ready for newer hardware when you’ve got it.

We’ll dive into IndexOfAny in much more depth in System.IO post. It’s a great API.


The .NET team has a “big tent” philosophy. We want every developer to find APIs that are approachable and suitable. If you are new to programming, we’ve got APIs for you. If you are more familiar with low-level APIs, we’ve got APIs that are likely familar.

I’m looking forward to sharing some in-depth analysis and exploration of the convenience spectrum in upcoming posts and hope that it leads to an an interesting discussion. If anything, this exercise has given me the insight on how much I appreciate the spectrum of these APIs. Perhaps our documentation should be updated to describe each topic area in terms of this specturm.

Thanks to David Fowler, Jan Kotas, and Stephen Toub for their help contributing to these posts.

You can keep up to date with this series by subscribing to the Convenience of .NET tag feed in your favorite RSS reader or subscribe to the entire blog via email below.


Comments are closed. Login to edit/delete your existing comments

  • Teddy Albina 11

    Dotnet core + is the best thing that happened to dotnet as a whole. It’s fast, modern, always improving. We are not tied to an operating system and can pick and choose our target and development platform. I can compile my dotnet app for Mac on my Windows Machine, we can leverage containers etc. It’s a superb piece of software. Dotnet Framework is obsolete and must be remove from any software.

  • Max Fletcher 7

    I just wanted to leave a comment as I also noticed how many people here seem to be complaining about .net core.

    As someone who really only started working with .NET a few years before .NET Core, the transition to core has made my life so much easier, core in particular was a huge improvement in my opinion. The amount of screwing around I used to have to do with web.config files and confusing differences between mvc and web api is all gone. All of the ways the app can be hosted in core is also massively simpler to actually implement.

    Please continue the work you are doing, I personally am not bothered if I feel like I can’t keep up 100%. I would rather have the platform move forward and have new things to learn than have it stagnate.

    Thank you.

  • mumtaz shah 0

    Great post. It would be perfect if dotnet’s official documentation had a toggle button or something for filtering — that we could turn on and then be presented with only the most convenient of APIs available in a particular type or library — without the distraction of going through lower level apis. It will save tons of time while rapid prototyping apps. Call the mode some good suggestive name , like Rapid Prototyping mode or something, lol.

  • LeaFrock 2

    Everyone speaks from their own interests. However, the times will eliminate some former vested interests.

    Many of the accusations in the comments section seem a bit funny at this point in time. They only see the difficulty of migration (and yet no one is pushing them with a gun) and only hope that there is something that can remain technically unchanged (or that they can be protected from change). In fact NFX is still maintained and is also working well. If it’s a decades-old project that pursues enterprise-level stability and security, then a migration to .NET Core is not necessary. Unrealistic migration pain is not a reason to belittle the achievements of .NET Core.

    Open source and cross-platform, almost standard in programming languages today. Open source brings a higher perspective, more knowledge, and more inspiration to the community, and the community has the same feedback. You may not even care about other developers’ desire to keep up with the times. And also not care what historical baggage NET Core gets rid of and what advanced changes it brings. What do you only care about? “Oh, I have 10+ years experiences of .NET. My project used WCF, but now .NET Core is not supported anymore! ” Hey, times have changed. Don’t live in the past and expect Microsoft to be a babysitter. Valuing and participating in the community is the future. For example, if you really want to migrate WCF, instead of complaining that there is no existing one, you should contribute to WCF.Core or examine gRPC. Either go on with NFX, and no one says it’s inappropriate. But the other way around, accuse .NET Core as a strategic mistake? Hey, the earth doesn’t revolve around you, does it?

    The progress .NET Core brings is bottom-up. A considerable part of it is impossible to implement on NFX. Technology does not have silver bullets, but only updates and follow-ups. As the old Chinese saying goes: it’s like sailing against the tide, and if it does not advance, it will retreat.

  • Simon Mourier 3

    I’ve been using all .NET versions professionaly from day 1 and I find it very convenient (build a web app for Windows and Linux so easily is what I call convenient for example).

    There’s one thing though that seems to be less and less convenient: deployment of apps in certain cases. This has sometimes become a nightmare, especially when .NET is mixed with other technologies, like the App Store, framework-dependent, single file only, AOT, publish config, MSIX, package vs non packaged, desktop bridge, WinUI3, MAUI, etc. And the tooling in this area doesn’t help. The worst part of it is it seems Microsoft doesn’t care and/or doesn’t even understand what we mean (ex: I can understand that because Microsoft usually deploys its software with big (huge) machinery (like Window, Office, etc.) so doesn’t really care about the details, but it’s definitly a problem for most of us. If we can’t deploy easily our software, no one will use it.

    I understand, it’s not exclusively .NET I’m speaking here, and teams responsible for all this are not the same (to the point we’re not always sure who to talk), but in the end, it’s somehow all what I call “.NET”.

  • Nigel Smith 0

    My Dell laptop was manufactured in 2021, but I ran the cpu-z app and it does say that it’s i7-1185G7 Processor supports the AVX-512 extensions. I ran a quick .NET 8 RC1 console app to check ‘Vector512.IsHardwareAccelerated’ and it was returning ‘True’. So I’m excited to try out the .NET 8.0 support for these AVX-512 extensions.
    And I look forward to seeing the other blog posts in your ‘Convenience of .NET’. It’s interesting to see the various APIs grouped by level of control & convenience, and I agree this would be useful to incorporate this into the official documentation.
    The company I work for uses Windows extensively, and I find .NET very versatile for wide range of applications at all levels of the stack. And I do enjoy using C# and Visual Studio, and I appreciate all the work that everyone at Microsoft is putting into all the developments.
    I do often still use the old .NET Framework, which is still fine for many legacy use cases. And its convenient that you can guarantee that .NET Framework 4.8 will be installed on Windows 10 & 11.
    But the new .NET like 6 and 7, and soon 8, are great for modern web based applications, with their excellent support for modern protocols. And the speed improvements with each new release are also very tempting.
    But the pace of change does often seem overwhelming from the point of keeping up-to-date with everything, but I guess that’s just a fact of modern computing life.

  • Midnight 1

    Looking forward when .NET will adopt gRPC to use as easy as HTTP now. I’m very familiar with network stack but some gRPC inconveniencies makes me cry 🙂 Also looking forward when WinUI 3/Windows App SDK will reach the quality of WPF and Winforms, for now to write pretty simple Desktop app is like a chalenge to fight with bugs and crashes. I won’t report them because I have no enought freee time to complete the proper bug report. But still .NET since Core 3.1 is pretty awesome as well as Team developing it. What can be more awesome than .NET 7? Easy! .NET 8!

    Microsoft, you have a great .NET as Core to develop anything. Develop the best Desktop SDK. We need .NET Desktop SDK branch to be as solid as Web. I know how to make the WinUI 3 the best: migrate Visual Studio 20xx there 🙂 The awesome Fluent Design in action, show us it above some more serious app than Windows Calculator/System Settings. Pay more attention to WinUI 3 please! MAUI? Linux please 🙂

  • Tore Lønne Senneseth 3

    I usually don’t engage in these types of public discussions, but in my experience, most of the criticism in this thread is unwarranted.

    This is my experience with .NET:
    I’ve been working with .NET since 1.0 (yes, I’m that old) and am currently working on a fairly large .NET 7 codebase (400+ projects).
    We migrated from .NET Framework 4.8 to .NET Core 3.0 fairly easily and have upgraded to new versions ever since with very little effort.
    Does our codebase use relatively simple features, making the move to new versions easy? I don’t think so. We develop software for finance, so our system must handle huge amounts of data very fast, with all the “usual” multi-user and multi-service concurrency and communication challenges. We do a lot of dynamic code generation and loading, IL-emitting, reflection and unsafe code to get max performance (the new low-level memory APIs has enabled us to use unsafe a lot less).

    With every new release of .NET, we’ve gotten a free performance boost simply because of the underlying improvements to the platform.
    With every new release, there’s been features and enhancement that has enabled us to do things better, simpler, and new things that we simply couldn’t do before.

    We have transitioned from running purely on-prem Windows VMs (IIS, Windows Services, SQL Server) to now run both on-prem and on Docker (Linux) + AKS as a SaaS offering – with the exact same codebase.

    In my opinion, because .NET has evolved significantly over the last years, it has enabled us to create better software.
    I can understand that the fast pace of releases and new patterns may seem overwhelming to some people, but I would much rather have a framework that evolves and stays current, than being stuck in old ways of doing things.
    We operate in a highly competitive landscape, and we need every bit of technological capability to stay in front.
    Keep in mind that you don’t HAVE to adopt a new API or pattern unless you want to. In my experience, .NET APIs extremely rarely break between upgrades.

    Although I may seem excessively positive about .NET, there’s obviously room for improvement. To me, the pain points has been the following:
    – The Azure teams at Microsoft tend to deprecate or make breaking changes to NuGet packages too frequently. However, it’s not completely fair to blame any of this on .NET pr se, because it’s the underlying services that change, so the APIs must change as well.
    – The identity / authentication story for (ASP).NET has been messy for years. .NET 8 is introducing new APIs, so let’s see how that works out.
    – WPF was (and still is) the best desktop framework ever created by Microsoft. This has been gravely mishandled by Microsoft.
    – Let’s not talk about Silverlight.

    Despite these last points, my opinion is that all the changes to .NET over the years has been overall very positive, enabling us to create much better software with every new .NET release.

  • ing. Miguel A. Aanguiano 2




    • Bela Istok 1

      @Miguel I have a WPF application that uses WCF for communicating with some external services we can not change, and is deployed with ClickOnce. This year was migrated from .NET Framework 4.8 to .NET 7 on Windows and is working as expected.

    • MJ 1

      So you don’t like the (new) .NET (core) LTS release cycle periods based on your experience with the old .NET Framework (the one bound to Windows only).

      If you were already on those .NET (core) LTS releases you would also know, they are generally pretty easy to upgrade to from other .NET (core) releases, compared to the past 4.x .NET Framework, where you are currently.

      It’s your own choice if you want to support what is today legacy .NET technologies, but that also means you are not likely to see much improvements to your current situation, unless you migrate over to the technologies used today. (It’s not Microsoft’s fault, things are moving fast, if they keep supporting legacy things, there wouldn’t be time to follow the trends of the rest of the market these days, leading to people abandoning C#/.NET, so you end up with a new VB situation, where no one really uses it for anything but legacy code bases.

  • Arun Kumar 1

    How Microsoft doing change’s in c# makes c# worst language , Linq and Task , await was awesome feature but current change’s useless , it’s not necessary to introduce changes in every release. People using c# for simplicity , fundamental change’s are totally worst. Java is not doing any stupid change’s.

    From many release’s I didn’t see any revolutionary feature instead of this you guys are playing with syntax.

    What is need of this below dam shit.

    public class BankAccount(string accountID, string owner) // How you get Convenience from this , If I tell you to eat your food with your feet instead of your hands, will it be easy for you?

    The day is not far when C# will be out of top 10 Index .

Feedback usabilla icon