Scott Hanselman

CoreBoy is a cross platform GameBoy Emulator written in C# that even does ASCII

April 23, '20 Comments [11] Posted in DotNetCore | Gaming | Open Source
Sponsored By

.NET and C# are great languages for programming emulators. Specifically retrogaming and retroarcade emulators. In fact, there's a long history of emulators written in C#. Here's just a few.

Today, David Whitney is deep into writing CoreBoy, a GameBoy Emulator written in C# and .NET Core, using WinForms, and I also spy the Avalonia cross-platform open source WPF-like framework. Head over to https://github.com/davidwhitney/CoreBoy and give the gent a STAR. It even has a headless mode and you could use it as a Library in your own software. Who doesn't want a GameBoy library in their app?

I cloned and built it with http://dot.net Core in just a few minutes. Lovely. I enjoy a clean codebase. Assuming you have a backup of one of the many physical GameBoy games you own like me, you can load a binary dump in CoreBoy as a *.gb or *.gbc file and you'll get something this:

CoreBoy - Zelda Link's Awakening

image

Sweet! Sure it's a little buggy and slow but figuring these things out is the fun of it all! I love that David Whitney is taking us on this journey with him.

There's even already a MonoGame-based graphics surface using DesktopGL and "nilllzz" has it running on Ubuntu!

GameBoy Emulator in C# running on Ubuntu using MonoGame

Emulators are always fun projects to read and learn from. Here, David has a clear separation of concerns between the emulator (handling the CPU, loading instructions, etc.) and the graphics surface that is ultimately responsible for putting pixels on screen.

It looks like he hasn't got it working yet (some issues with command line parsing), but in a few minutes with a little hard-coding I was able to switch to ASCII mode with David's SillyAsciiArtCreator that takes a Pixel and RGB value and maps it to ASCII art that looks awesome in the Windows Terminal.

Zelda in a GameBoy Emulator as ASCII Art

Which is kind of awesome. Why would you do this? BECAUSE YOU CAN

Zelda in a GameBoy Emulator as ASCII Art

I look forward to seeing what comes of this cool new emulator and I'll be reading its code in more detail in the weeks to come! Great stuff, David!


Sponsor: Couchbase gives developers the power of SQL with the flexibility of JSON. Start using it today for free with technologies including Kubernetes, Java, .NET, JavaScript, Go, and Python.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Adding a git commit hash and Azure DevOps Build Number and Build ID to an ASP.NET website

March 6, '20 Comments [15] Posted in ASP.NET | DotNetCore
Sponsored By

imageA few months back I moved my CI/CD (Continuous Integration/Continuous Development) to Azure DevOps for free. You get 1800 build minutes a month FREE and I'm not even close to using it with three occasionally-updated sites building on it. Earlier this week I wrote about making a cleaner and more intentional azure-pipelines.yml for an ASP.NET Core Web App

I was working/pairing with Damian today because I wanted to get my git commit hashes and build ids embedded into the actual website so I could see exactly what commit is in production.

That's live on hanselminutes.com righ tnow and looks like this

© Copyright 2020, Scott Hanselman. Design by @jzy, Powered by .NET Core 3.1.2 and deployed from commit 6b48de via build 20200310.7

There's a few things here and it's all in my ASP.NET Web App's main layout page called _layout.cshtml. You can look all about ASP.NET Core 101, .NET and C# over at https://dot.net/videos if you'd like. They've lovely videos.

My website footer has git commits

So let's take this footer apart, shall we?

<div class="copyright">&copy; Copyright @DateTime.Now.Year, 
<a href="https://www.hanselman.com">Scott Hanselman</a>.
Design by <a href="http://www.8164.org/">@@jzy</a>,
Powered by @System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription
and deployed from commit <a href="https://github.com/shanselman/hanselminutes-core/commit/@appInfo.GitHash">@appInfo.ShortGitHash</a>
via build <a href="https://dev.azure.com/hanselman/Hanselminutes%20Website/_build/results?buildId=@appInfo.BuildId&view=results">@appInfo.BuildNumber</a>
</div>

First, the obvious floating copyright year. Then a few credits that are hard coded.

Next, a call to @System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription which gives me this string ".NET Core 3.1.2" Note that there was a time for a while where that Property was somewhat goofy, but no longer.

I have two kinds of things I want to store along with my build artifact and output.

  • I want the the Git commit hash of the code that was deployed.
    • Then I want to link it back to my source control. Note that my site is a private repo so you'll get a 404
  • I want the Build Number and the Build ID
    • This way I can link back to my Azure DevOps site

Adding a Git Commit Hash to your .NET assembly

There's lots of Assembly-level attributes you can add to your .NET assembly. One lovely one is AssemblyInformationalVersion and if you pass in SourceRevisionId on the dotnet build command line, it shows up in there automatically. Here's an example:

[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+d6b3d432970c9acbc21ecd22c9f5578892385305")]
[assembly: AssemblyProduct("hanselminutes.core")]
[assembly: AssemblyTitle("hanselminutes.core")]
[assembly: AssemblyVersion("1.0.0.0")]

From this command line:

dotnet build --configuration Release /p:SourceRevisionId=d6b3d432970c9acbc21ecd22c9f5578892385305

But where does that hash come from? Well, Azure Dev Ops includes it in an environment variable so you can make a YAML task like this:

- task: DotNetCoreCLI@2
displayName: 'dotnet build $(buildConfiguration)'
inputs:
command: 'build'
arguments: '-r $(rid) --configuration $(buildConfiguration) /p:SourceRevisionId=$(Build.SourceVersion)'

Sweet. That will put in VERSION+HASH, so we'll pull that out of a utility class Damian made like this (full class will be shown later)

public string GitHash
{
get
{
if (string.IsNullOrEmpty(_gitHash))
{
var version = "1.0.0+LOCALBUILD"; // Dummy version for local dev
var appAssembly = typeof(AppVersionInfo).Assembly;
var infoVerAttr = (AssemblyInformationalVersionAttribute)appAssembly
.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute)).FirstOrDefault();

if (infoVerAttr != null && infoVerAttr.InformationalVersion.Length > 6)
{
// Hash is embedded in the version after a '+' symbol, e.g. 1.0.0+a34a913742f8845d3da5309b7b17242222d41a21
version = infoVerAttr.InformationalVersion;
}
_gitHash = version.Substring(version.IndexOf('+') + 1);

}

return _gitHash;
}
}

Displaying it is then trivial given the helper class we'll see in a minute. Note that hardcoded paths for my private repo. No need to make things complex.

deployed from commit <a href="https://github.com/shanselman/hanselminutes-core/commit/@appInfo.GitHash">@appInfo.ShortGitHash</a>

Getting and Displaying Azure DevOps Build Number and Build ID

This one is a little more complex. We could theoretically tunnel this info into an assembly as well but it's just as easy, if not easier to put it into a text file and make sure it's part of the ContentRootPath (meaning it's just in the root of the website's folder).

To be clear, an option: There are ways to put this info in an Attribute but not without messing around with your csproj using some not-well-documented stuff. I like a clean csproj so I like this. Ideally there'd be another thing like SourceRevisionID to carry this metadata.

You'd need to do something like this, and then pull it out with reflection. Meh.

<ItemGroup>
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute" Condition="$(BuildNumber) != ''" >
<_Parameter1>BuildNumber</_Parameter1>
<_Parameter2>$(BuildNumber)</_Parameter2>
</AssemblyAttribute>
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute" Condition="$(BuildId) != ''" >
<_Parameter1>BuildId</_Parameter1>
<_Parameter2>$(BuildId)</_Parameter2>
</AssemblyAttribute>
</ItemGroup>

Those $(BuildNumber) and $(BuildId) dealies are build variables. Again, this csproj messing around is not for me.

Instead, a simple text file, coming along for the ride.

- script: 'echo -e "$(Build.BuildNumber)\n$(Build.BuildId)" > .buildinfo.json'
displayName: "Emit build number"
workingDirectory: '$(Build.SourcesDirectory)/hanselminutes.core'
failOnStderr: true

I'm cheating a little as I gave it the .json extension, only because JSON files are copying and brought along as "Content." If it didn't have an extension I would need to copy it manually, again, with my csproj:

<ItemGroup>
<Content Include=".buildinfo">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>

So, to be clear, two build variables inside a little text file. Then make a little helper class from Damian. Again, that file is in ContentRootPath and was zipped up and deployed with our web app.

public class AppVersionInfo
{
private static readonly string _buildFileName = ".buildinfo.json";
private string _buildFilePath;
private string _buildNumber;
private string _buildId;
private string _gitHash;
private string _gitShortHash;

public AppVersionInfo(IHostEnvironment hostEnvironment)
{
_buildFilePath = Path.Combine(hostEnvironment.ContentRootPath, _buildFileName);
}

public string BuildNumber
{
get
{
// Build number format should be yyyyMMdd.# (e.g. 20200308.1)
if (string.IsNullOrEmpty(_buildNumber))
{
if (File.Exists(_buildFilePath))
{
var fileContents = File.ReadLines(_buildFilePath).ToList();

// First line is build number, second is build id
if (fileContents.Count > 0)
{
_buildNumber = fileContents[0];
}
if (fileContents.Count > 1)
{
_buildId = fileContents[1];
}
}

if (string.IsNullOrEmpty(_buildNumber))
{
_buildNumber = DateTime.UtcNow.ToString("yyyyMMdd") + ".0";
}

if (string.IsNullOrEmpty(_buildId))
{
_buildId = "123456";
}
}

return _buildNumber;
}
}

public string BuildId
{
get
{
if (string.IsNullOrEmpty(_buildId))
{
var _ = BuildNumber;
}

return _buildId;
}
}

public string GitHash
{
get
{
if (string.IsNullOrEmpty(_gitHash))
{
var version = "1.0.0+LOCALBUILD"; // Dummy version for local dev
var appAssembly = typeof(AppVersionInfo).Assembly;
var infoVerAttr = (AssemblyInformationalVersionAttribute)appAssembly
.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute)).FirstOrDefault();

if (infoVerAttr != null && infoVerAttr.InformationalVersion.Length > 6)
{
// Hash is embedded in the version after a '+' symbol, e.g. 1.0.0+a34a913742f8845d3da5309b7b17242222d41a21
version = infoVerAttr.InformationalVersion;
}
_gitHash = version.Substring(version.IndexOf('+') + 1);

}

return _gitHash;
}
}

public string ShortGitHash
{
get
{
if (string.IsNullOrEmpty(_gitShortHash))
{
_gitShortHash = GitHash.Substring(GitHash.Length - 6, 6);
}
return _gitShortHash;
}
}
}

How do we access this class? Simple! It's a Singleton added in one line in Startup.cs's ConfigureServices():

services.AddSingleton<AppVersionInfo>();

Then injected in one line in our _layout.cshtml!

@inject AppVersionInfo appInfo

Then I can use it and it's easy. I could put an environment tag around it to make it only show up in staging:

<environment include="Staging">
<cache expires-after="@TimeSpan.FromDays(30)">
<div class="copyright">&copy; Copyright @DateTime.Now.Year, <a href="https://www.hanselman.com">Scott Hanselman</a>. Design by <a href="http://www.8164.org/">@@jzy</a>, Powered by @System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription and deployed from commit <a href="https://github.com/shanselman/hanselminutes-core/commit/@appInfo.GitHash">@appInfo.ShortGitHash</a> via build <a href="https://dev.azure.com/hanselman/Hanselminutes%20Website/_build/results?buildId=@appInfo.BuildId&view=results">@appInfo.BuildNumber</a> </div>
</cache>
</environment>

I could also wrap it all in a cache tag like this. Worst case for a few days/weeks at the start of a new year the Year is off.

<cache expires-after="@TimeSpan.FromDays(30)">

<cache>

Thoughts on this technique?


Sponsor: This week's sponsor is...me! This blog and my podcast has been a labor of love for over 18 years. Your sponsorship pays my hosting bills for both AND allows me to buy gadgets to review AND the occasional taco. Join me!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Making a cleaner and more intentional azure-pipelines.yml for an ASP.NET Core Web App

March 4, '20 Comments [10] Posted in Azure | DotNetCore
Sponsored By

Azure Pipelines releasing to LinuxA few months back I moved my CI/CD (Continuous Integration/Continuous Development) to Azure DevOps for free. You get 1800 build minutes a month FREE and I'm not even close to using it with three occasionally-updated sites building on it.

It wasn't too hard, but as with all build pipelines you'll end up with a bunch of trial and error builds until you really get it dialed in.

I was working/pairing with Damian today because I wanted to get my git commit hashes and build ids embedded into the actual website so I could see exactly what commit is in production. How to do that will be the next post!

However, while tidying up we noticed some possible speed up and potential issues with my original azurepipeslines.yml file, so here's my new one!

NOTE: There's MANY ways to write one of these. For example, note that I'm allowing the "dotnet restore" to happen automatically as a sign effect of the call to dotnet build. Damian prefers to make that more explicit as its own task so he can see timing info for it. It's up to you, just know the side effects and measure!

Let's read the YAML and see what's up here.

  • My primary Git branch is called "main" so my Pipeline triggers on commits to main.
  • I'm using a VM from the pool that's the latest Ubuntu.
  • I'm doing a Release (not Debug) build and putting that value in a variable that I can use later in the pipeline.
  • I'm using a "runtime id" of linux-x64 and I'm storing that value also for use later. That's the .NET Core runtime I'm interested in.
  • I'm passing in the -r $(rid) to be absolutely clear about my intent at every step.
  • I want to build ONCE so I'm using --no-build on the publish command. It's likely not needed, but because I was using a rid on the build and then not using it later, my publish was wasting time by building again.
  • The dotnet test command uses -r for results (dumb) so I have to pass in --runtime if I want to pass in a rid. Again, likely not needed, but it's explicit.
  • I publish and name the artifact (fancy word for the resulting ZIP file) so it can be used later in the Deployment pipeline.

Here's the YAML

# https://docs.microsoft.com/azure/devops/pipelines/languages/dotnet-core

trigger:
- main

pool:
vmImage: 'ubuntu-latest'

variables:
buildConfiguration: 'Release'
rid: 'linux-x64'

steps:
- task: UseDotNet@2
inputs:
version: '3.1.x'
packageType: sdk

- task: DotNetCoreCLI@2
displayName: 'dotnet build $(buildConfiguration)'
inputs:
command: 'build'
arguments: '-r $(rid) --configuration $(buildConfiguration) /p:SourceRevisionId=$(Build.SourceVersion)'

- task: DotNetCoreCLI@2
displayName: "Test"
inputs:
command: test
projects: '**/*tests/*.csproj'
arguments: '--runtime $(rid) --configuration $(buildConfiguration)'

- task: DotNetCoreCLI@2
displayName: "Publish"
inputs:
command: 'publish'
publishWebProjects: true
arguments: '-r $(rid) --no-build --configuration $(BuildConfiguration) --output $(Build.ArtifactStagingDirectory)'
zipAfterPublish: true

- task: PublishBuildArtifacts@1
displayName: "Upload Artifacts"
inputs:
pathtoPublish: '$(Build.ArtifactStagingDirectory)'
artifactName: 'hanselminutes'

Did I miss anything? What are your best tips for a clean YAML file that you can use to build and deploy a .NET Web app?


Sponsor: This week's sponsor is...me! This blog and my podcast has been a labor of love for over 18 years. Your sponsorship pays my hosting bills for both AND allows me to buy gadgets to review AND the occasional taco. Join me!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Announcing .NET Interactive - Try .NET includes .NET Notebooks and more

February 12, '20 Comments [11] Posted in DotNetCore | Open Source
Sponsored By

At Microsoft Ignite 2019, we were happy to announce that the "Try .NET global tool" added support for C# and F# Jupyter notebooks. Last week, the same team that brought you .NET Notebooks announced Preview 2 of the .NET Notebook.

Name Change - .NET interactive

As the scenarios for what was "Try .NET" continued to grow, the team wanted to a name that encompassed all the experiences they have as well as all the experiences they will have in the future. What was the Try .NET family of projects is now .NET interactive.

The F# community has enjoyed F# in Juypter Notebooks from years with the pioneering functional work of Rick Minerich, Colin Gravill and many other contributors! .NET Interactive is a family of tools and kernels that offer support across a variety of experiences as a 1st party Microsoft-supported offering.

.NET interactive is a group of CLI (command line interface) tools and APIs that enable users to create interactive experiences across the web, markdown, and notebooks.

.NET Interactive APIs and Tools

Here is what the command line looks like using the dotnet CLI.

  • dotnet interactive global tool:
  • dotnet try global tool:
    • Used for workshops and offline documentation. Interactive markdown with a backing project. I wrote about this in May 2019.
  • trydotnet.js API
    • Currently, only used internally at Microsoft, this API is used on the .NET page and C# documentation. Maybe one day I can use it on my blog? And yours?

Installing .NET Interactive

You can start playing with it today, locally or in the cloud! Seriously. Just click and start using it.

Before you install the .NET interactive global tool, please make sure you have the following:

> jupyter kernelspec list
  python3        ~\jupyter\kernels\python3
  • Open Windows terminal and install the dotnet interactive global tool:
> dotnet tool install --global Microsoft.dotnet-interactive
  • Switch back to Anaconda prompt and install the .NET kernel. To be clear, here we are using the dotnet CLI to let the Jupyter CLI know that we exist!
> dotnet interactive jupyter install
[InstallKernelSpec] Installed kernelspec .net-csharp in ~\jupyter\kernels\.net-csharp
.NET kernel installation succeeded

[InstallKernelSpec] Installed kernelspec .net-fsharp in ~\jupyter\kernels\.net-fsharp
.NET kernel installation succeeded

[InstallKernelSpec] Installed kernelspec .net-powershell in ~\jupyter\kernels\.net-powershell
.NET kernel installation succeeded
  • While still in Anaconda prompt, verify that .NET kernel is installed like this
> jupyter kernelspec list
  .net-csharp     ~\jupyter\kernels\.net-csharp
  .net-fsharp     ~\jupyter\kernels\.net-fsharp
  .net-powershell ~\jupyter\kernels\.net-powershell
  python3         ~\jupyter\kernels\python3

Now you can just run "jupyter lab" at the command line and you're ready to go!

More Languages - PowerShell

The .NET kernel now comes PowerShell support too! In Preview 2, the .NET interactive team partnered with PowerShell to enable this scenario. You can read more about the announcement of the PowerShell blog.

.NET in Jupyter Notebooks

The .NET interactive team is looking forward to hearing your thoughts. You can talk to them at https://github.com/dotnet/interactive

Multi .NET language Notebooks

I wanted to highlight one of the hidden gems .NET interactive has had since Preview 1 - multi-language notebooks. That means that users can switch languages in a single notebook. Here is an example of a C#, F#, and PowerShell in a single .ipynb file.

Multiple Language Notebooks

Using one of the language magic commands (#!csharp, #!fsharp,#pwsh) tells the .NET Interactive kernel to run the cell in a specific language. To see a complete list of the available magic commands, enter the #!lsmagic command into a new cell and run it.

.NET Code in nteract.io

Additionally, you can now write .NET Code in nteract.io. Nteract is an open-source organization that builds SDKs, applications, and libraries that helps people make the most of interactive notebooks and REPLs. We are excited to have our .NET users take advantage of the rich REPL experience nteract provides, including the nteract desktop app.

Charts and graphs in nteract

To get started with .NET Interactive in nteract please download the nteract desktop app and install the .NET kernels.

Learn More

The team is looking forward to seeing what you build. Moving forward, the team has split dotnet try and dotnet interactive tools into separate repos.

  • For any issues, feature requests, and contributions to .NET Notebooks, please visit the .NET Interactive repo.
  • For any issues, feature requests, and contributions on interactive markdown and trydotnet.js, please visit the Try .NET repo.

Sponsor: Have you tried developing in Rider yet? This fast and feature-rich cross-platform IDE improves your code for .NET, ASP.NET, .NET Core, Xamarin, and Unity applications on Windows, Mac, and Linux.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb

Your Todo application is too complex or not complex enough

February 7, '20 Comments [2] Posted in DotNetCore | Open Source
Sponsored By

Reading Code is FunI've blogged before about ASP.NET Architect David Fowler's hidden gems in ASP.NET. His GitHub is worth following because he's always exploring and thinking and he's doing it in public. I love reading other people's source code.

He's been working on a local orchestrator called Micronetes that is worth reading about, but for this blog post I want to focus on his "Todos" repository.

Making a Todo List is a form of Hello World on the web, similar to making a blog or a simple website. Everyone knows what a Todo app should look and act like, so you can just focus on your tools and not on the requirements. You may feel that a Todo app "isn't complex enough" or isn't a good example app to make. That's fine, but it is worth exploring and reading the different ways the same thing can be done.

David's repository https://github.com/davidfowl/Todos is of note because it's not ONE Todo App. As of the time of this writing it's 8 todo apps, each with a different reason to exist.

What's a basic app look like? What if you add Auth? What if you add Dependency Injection? What about Controllers? You get the idea.

Some languages and platforms (*ahem* enterprise) get a reputation for being too complex, too layered, too many projects. Others may get the opposite reputation - that's a toy, it'll never scale (in size, traffic, size of team, whatever).

The point is that not everything is a hammer and not everything is a screw. You may think this is a cop out, but the answers is always "It depends." The more experience you get in software and the more mistakes you make and the more systems you put into production the more you'll realize that - wait for it - it depends. Disagree if you like, but one size doesn't fit all.

Some cool stuff about David's Todo code

All that said, there's some cool "before and afters" if you look at the code for earlier ideomatic C# and what newer APIs and language features allow. For example, if we assume some extensions and new APIs added for clarity, here's a POST

static async Task PostAsync(HttpContext context)
{
var todo = await context.Request.ReadJsonAsync<Todo>(_options);

using var db = new TodoDbContext();
await db.Todos.AddAsync(todo);
await db.SaveChangesAsync();

context.Response.StatusCode = StatusCodes.Status204NoContent;
}

and the GET

static async Task GetAllAsync(HttpContext context)
{
using var db = new TodoDbContext();
var todos = await db.Todos.ToListAsync();

await context.Response.WriteJsonAsync(todos, _options);
}

I personally do think that stuff like this is too complex. I hate that out parameter.

static async Task GetAsync(HttpContext context)
{
if (!context.Request.RouteValues.TryGet("id", out long id))
{
context.Response.StatusCode = StatusCodes.Status400BadRequest;
return;
}

using var db = new TodoDbContext();
var todo = await db.Todos.FindAsync(id);
if (todo == null)
{
context.Response.StatusCode = StatusCodes.Status404NotFound;
return;
}

await context.Response.WriteJsonAsync(todo);
}

This is made-up code from me that doesn't work. It's even still a little too much.

static async Task GetAsync(HttpContext context)
{
if (!RouteValues.Exist("id")) return Http.400;

using var db = new TodoDbContext();
var todo = await db.Todos.FindAsync(RouteValues["id"] as int);
if (todo == null) return Http.404

await Json(todo);
}

These are all useful exercises and are fun to explore. It also brings up some hard questions:

  • What is the difference between terse and clear versus obscure and inaccessible?
  • How important is the Law of Demeter?
  • Are some problems better solved by language changes or by main library changes?
  • How many things should/can be put into extension methods?
    • And when those basic scenarios break down, are you dropped into a Func<T<T<T<T<T<T>>>>> hellscape?

Do you enjoy reading code like this as much as I do, Dear Reader? I think it's a great learning tool. I could do a whole day-long class facilitating conversation around this code https://github.com/davidfowl/Todos

Enjoy!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb
Page 1 of 27 in the DotNetCore category Next Page

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.