Initial commit
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
.DS_Store
|
||||
50
README.md
|
|
@ -1,2 +1,48 @@
|
|||
# cs11dotnet7
|
||||
Repository for the Packt Publishing book titled "C# 11 and .NET 7 - Modern Cross-Platform Development" by Mark J. Price
|
||||
[](https://open.vscode.dev/markjprice/cs11dotnet7)
|
||||
|
||||
[Improvements & Errata - list of corrections](errata.md)
|
||||
|
||||
# C# 11 and .NET 7 - Modern Cross-Platform Development, Seventh Edition
|
||||
|
||||
The previous edition was published on Tuesday, November 9, 2021. Order today from [Packt](https://www.packtpub.com/product/c-10-and-net-6-modern-cross-platform-development-sixth-edition/9781801077361), [Amazon.com](https://www.amazon.com/10-NET-Cross-Platform-Development-websites-dp-1801077363/dp/1801077363/), [Amazon.co.uk](https://www.amazon.co.uk/10-NET-Cross-Platform-Development-websites-dp-1801077363/dp/1801077363/), [Amazon.com.au](https://www.amazon.com.au/10-NET-Cross-Platform-Development-websites-ebook/dp/B09JV37DM6/), and all good book stores. My author page on Amazon: https://www.amazon.com/Mark-J-Price/e/B071DW3QGN/
|
||||
|
||||
## Code solutions for Visual Studio 2022 and Visual Studio Code
|
||||
|
||||
If you have Visual Studio 2022 for Windows and Visual Studio Code and its C# extension installed on the same computer, the build process can sometimes conflict. This is because Visual Studio has its own non-standard build server that is different from the standard build server used by .NET SDK. Visual Studio 2022 and Visual Studio Code also use different file types to group projects. So I have duplicated and separated solution code for each product:
|
||||
|
||||
- [/vs4win](/vs4win): Visual Studio 2022 for Windows solution files and projects for each chapter
|
||||
- [/vscode](/vscode): Visual Studio Code workspace files and projects for each chapter
|
||||
|
||||
## Bonus content
|
||||
The appendix is available to download as a PDF:
|
||||
- Appendix A, Answers to the Test Your Knowledge Questions (coming November 2022)
|
||||
|
||||
Supplementary print book materials available to download:
|
||||
- Color images of the screenshots/diagrams used in this book (coming November 2022)
|
||||
|
||||
## Important
|
||||
Corrections for typos and other mistakes and improvements like refactoring code. Useful links to other related material.
|
||||
- [Book Links](book-links.md)
|
||||
- [Improvements & Errata - list of corrections](errata.md)
|
||||
|
||||
## Microsoft .NET community support
|
||||
- .NET Developer Community: https://dotnet.microsoft.com/platform/community
|
||||
- .NET Tech Community Forums for topic discussions: https://techcommunity.microsoft.com/t5/net/ct-p/dotnet
|
||||
- Q&A for .NET to get your questions answered: https://docs.microsoft.com/en-us/answers/products/dotnet
|
||||
- Technical questions about the C# programming language: https://docs.microsoft.com/en-us/answers/topics/dotnet-csharp.html
|
||||
- If you'd like to apply to be a reviewer: https://authors.packtpub.com/reviewers/
|
||||
|
||||
## Interviews with me
|
||||
Podcast interviews with me:
|
||||
- [The .NET Core Podcast - February 4, 2022](https://dotnetcore.show/episode-91-c-sharp-10-and-dotnet-6-with-mark-j-price/)
|
||||
- [The .NET Core Podcast - February 7, 2020](https://dotnetcore.show/episode-44-learning-net-core-with-mark-j-price/)
|
||||
- [Yet Another Podcast with Jesse Liberty - May 2021](http://jesseliberty.com/2021/05/16/mark-price-on-c9-and-net-6/)
|
||||
- [Yet Another Podcast with Jesse Liberty - February 2020](http://jesseliberty.com/2020/02/23/mark-price-c-net-core/)
|
||||
- [Packt Podcasts](https://soundcloud.com/packt-podcasts/csharp-8-dotnet-core-3-the-evolution-of-the-microsoft-ecosystem)
|
||||
|
||||
Written interviews with me:
|
||||
- [C# 9 and .NET 5: Book Review and Q&A](https://www.infoq.com/articles/book-interview-mark-price/?itm_source=infoq&itm_campaign=user_page&itm_medium=link)
|
||||
- [Q&A with Episerver's Mark J. Price, author of C# 9 and .NET 5 - Modern Cross-Platform Development](https://www.episerver.com/articles/q-and-a-with-mark-price)
|
||||
|
||||
## Book cover
|
||||

|
||||
|
|
|
|||
918
book-links.md
Normal file
|
|
@ -0,0 +1,918 @@
|
|||
- [Chapter 1 - Hello, C#! Welcome, .NET!](#chapter-1---hello-c-welcome-net)
|
||||
- [Visual Studio 2022 for Windows](#visual-studio-2022-for-windows)
|
||||
- [Visual Studio Code](#visual-studio-code)
|
||||
- [Other C# code editors and platforms](#other-c-code-editors-and-platforms)
|
||||
- [.NET Interactive](#net-interactive)
|
||||
- [Command Line Interfaces](#command-line-interfaces)
|
||||
- [.NET](#net)
|
||||
- [Open source and other projects related to .NET](#open-source-and-other-projects-related-to-net)
|
||||
- [Git](#git)
|
||||
- [Help and learning](#help-and-learning)
|
||||
- [Chapter 2 - Speaking C](#chapter-2---speaking-c)
|
||||
- [C# language version and the journey to C# 10](#c-language-version-and-the-journey-to-c-10)
|
||||
- [C# language](#c-language)
|
||||
- [C# implemented proposals](#c-implemented-proposals)
|
||||
- [C# proposals being worked on](#c-proposals-being-worked-on)
|
||||
- [Text types](#text-types)
|
||||
- [Number types](#number-types)
|
||||
- [Console apps and formatting](#console-apps-and-formatting)
|
||||
- [Miscellaneous](#miscellaneous)
|
||||
- [Chapter 3 - Controlling Flow and Converting Types](#chapter-3---controlling-flow-and-converting-types)
|
||||
- [Operators](#operators)
|
||||
- [Branching statements and pattern matching](#branching-statements-and-pattern-matching)
|
||||
- [Casting, converting, rounding, and formatting data](#casting-converting-rounding-and-formatting-data)
|
||||
- [Handling exceptions](#handling-exceptions)
|
||||
- [Coding problems in interviews](#coding-problems-in-interviews)
|
||||
- [Chapter 4 - Writing, Debugging, and Testing Functions](#chapter-4---writing-debugging-and-testing-functions)
|
||||
- [Concepts](#concepts)
|
||||
- [Debugging](#debugging)
|
||||
- [Instrumenting](#instrumenting)
|
||||
- [Testing](#testing)
|
||||
- [Chapter 5 - Building Your Own Types with Object-Oriented Programming](#chapter-5---building-your-own-types-with-object-oriented-programming)
|
||||
- [Fields](#fields)
|
||||
- [Methods](#methods)
|
||||
- [Properties](#properties)
|
||||
- [Pattern matching](#pattern-matching)
|
||||
- [Records](#records)
|
||||
- [Miscellaneous](#miscellaneous-1)
|
||||
- [Chapter 6 - Implementing Interfaces and Inheriting Classes](#chapter-6---implementing-interfaces-and-inheriting-classes)
|
||||
- [Operator overloading](#operator-overloading)
|
||||
- [Delegates and events](#delegates-and-events)
|
||||
- [Interfaces and generics](#interfaces-and-generics)
|
||||
- [Memory, reference and value types](#memory-reference-and-value-types)
|
||||
- [Null and nullability](#null-and-nullability)
|
||||
- [Inheritance](#inheritance)
|
||||
- [Chapter 7 - Packaging and Distributing .NET Types](#chapter-7---packaging-and-distributing-net-types)
|
||||
- [.NET Standard](#net-standard)
|
||||
- [.NET versions](#net-versions)
|
||||
- [.NET API, packages and frameworks](#net-api-packages-and-frameworks)
|
||||
- [Roslyn, SDKs, and project templates](#roslyn-sdks-and-project-templates)
|
||||
- [Packaging and publishing](#packaging-and-publishing)
|
||||
- [Decompiling .NET](#decompiling-net)
|
||||
- [Porting from .NET Framework to modern .NET](#porting-from-net-framework-to-modern-net)
|
||||
- [Preview features](#preview-features)
|
||||
- [Chapter 8 - Working with Common .NET Types](#chapter-8---working-with-common-net-types)
|
||||
- [Working with numbers](#working-with-numbers)
|
||||
- [Working with text and regular expressions](#working-with-text-and-regular-expressions)
|
||||
- [Dates and times](#dates-and-times)
|
||||
- [Collections](#collections)
|
||||
- [Spans](#spans)
|
||||
- [Networking](#networking)
|
||||
- [Assemblies and reflection](#assemblies-and-reflection)
|
||||
- [Working with images](#working-with-images)
|
||||
- [Internationalization](#internationalization)
|
||||
- [Chapter 9 - Working with Files, Streams, and Serialization](#chapter-9---working-with-files-streams-and-serialization)
|
||||
- [File system, encoding, and streams](#file-system-encoding-and-streams)
|
||||
- [Serialization](#serialization)
|
||||
- [JSON](#json)
|
||||
- [Chapter 10 - Working with Data Using Entity Framework Core](#chapter-10---working-with-data-using-entity-framework-core)
|
||||
- [Legacy EF 6](#legacy-ef-6)
|
||||
- [EF Core](#ef-core)
|
||||
- [EF Core database providers](#ef-core-database-providers)
|
||||
- [SQLite](#sqlite)
|
||||
- [Microsoft SQL Server](#microsoft-sql-server)
|
||||
- [NoSQL data stores](#nosql-data-stores)
|
||||
- [EF Core models](#ef-core-models)
|
||||
- [EF Core querying and manipulating](#ef-core-querying-and-manipulating)
|
||||
- [Chapter 11 - Querying and Manipulating Data Using LINQ](#chapter-11---querying-and-manipulating-data-using-linq)
|
||||
- [LINQ concepts](#linq-concepts)
|
||||
- [LINQ providers](#linq-providers)
|
||||
- [LINQ samples and tools](#linq-samples-and-tools)
|
||||
- [Chapter 12 - Improving Performance and Scalability Using Multitasking](#chapter-12---improving-performance-and-scalability-using-multitasking)
|
||||
- [Threads](#threads)
|
||||
- [Tasks](#tasks)
|
||||
- [Thread safety](#thread-safety)
|
||||
- [async and await](#async-and-await)
|
||||
- [Parallel programming](#parallel-programming)
|
||||
- [Sharing resouces and synchronization](#sharing-resouces-and-synchronization)
|
||||
- [Chapter 13 - Introducing Practical Applications of C# and .NET](#chapter-13---introducing-practical-applications-of-c-and-net)
|
||||
- [.NET apps](#net-apps)
|
||||
- [.NET Content Management Systems](#net-content-management-systems)
|
||||
- [ASP.NET versions and features](#aspnet-versions-and-features)
|
||||
- [Web development technologies](#web-development-technologies)
|
||||
- [Windows desktop app development](#windows-desktop-app-development)
|
||||
- [Third-party cross-platform GUI development](#third-party-cross-platform-gui-development)
|
||||
- [Chapter 14 - Building Websites Using ASP.NET Core Razor Pages](#chapter-14---building-websites-using-aspnet-core-razor-pages)
|
||||
- [General web development](#general-web-development)
|
||||
- [ASP.NET Core](#aspnet-core)
|
||||
- [Razor Pages and layouts](#razor-pages-and-layouts)
|
||||
- [Endpoint routing](#endpoint-routing)
|
||||
- [Chapter 15 - Building Websites Using the Model-View-Controller Pattern](#chapter-15---building-websites-using-the-model-view-controller-pattern)
|
||||
- [Security and privacy](#security-and-privacy)
|
||||
- [Setting up and configuring](#setting-up-and-configuring)
|
||||
- [Controllers](#controllers)
|
||||
- [Models](#models)
|
||||
- [Views](#views)
|
||||
- [Miscellaneous](#miscellaneous-2)
|
||||
- [Chapter 16 - Building and Consuming Web Services](#chapter-16---building-and-consuming-web-services)
|
||||
- [Web service technologies](#web-service-technologies)
|
||||
- [Web service routing](#web-service-routing)
|
||||
- [Web service clients](#web-service-clients)
|
||||
- [Documenting web services](#documenting-web-services)
|
||||
- [Securing web services](#securing-web-services)
|
||||
- [Health checks and reliable web services](#health-checks-and-reliable-web-services)
|
||||
- [Chapter 17 - Building User Interfaces Using Blazor](#chapter-17---building-user-interfaces-using-blazor)
|
||||
- [Blazor hosting models](#blazor-hosting-models)
|
||||
- [Blazor components](#blazor-components)
|
||||
- [Advanced techniques](#advanced-techniques)
|
||||
- [Other resources](#other-resources)
|
||||
- [Chapter 18 - Building and Consuming Other Services](#chapter-18---building-and-consuming-other-services)
|
||||
- [WCF](#wcf)
|
||||
- [gRPC](#grpc)
|
||||
- [SignalR](#signalr)
|
||||
- [OData](#odata)
|
||||
- [Azure Functions](#azure-functions)
|
||||
- [Chapter 19 - Building Mobile and Desktop Apps Using .NET MAUI](#chapter-19---building-mobile-and-desktop-apps-using-net-maui)
|
||||
- [.NET MAUI](#net-maui)
|
||||
- [Mobile development](#mobile-development)
|
||||
- [Chapter 20 - Protecting Your Data and Applications](#chapter-20---protecting-your-data-and-applications)
|
||||
- [Cross-platform cryptography](#cross-platform-cryptography)
|
||||
- [General security knowledge](#general-security-knowledge)
|
||||
- [Encryption](#encryption)
|
||||
- [Epilogue](#epilogue)
|
||||
- [Next steps on your C# and .NET learning journey](#next-steps-on-your-c-and-net-learning-journey)
|
||||
- [Learn from other Packt books](#learn-from-other-packt-books)
|
||||
|
||||
# Chapter 1 - Hello, C#! Welcome, .NET!
|
||||
|
||||
## Visual Studio 2022 for Windows
|
||||
- Download Visual Studio for Windows: https://visualstudio.microsoft.com/downloads/
|
||||
- Sign up for a Microsoft account: https://signup.live.com/
|
||||
- Visual Studio for Windows documentation: https://docs.microsoft.com/en-us/visualstudio/windows/
|
||||
- MSBuild and 64-bit Visual Studio 2022: https://devblogs.microsoft.com/dotnet/msbuild-and-64-bit-visual-studio-2022/
|
||||
- Create C# apps with Visual Studio: https://docs.microsoft.com/en-us/visualstudio/get-started/csharp/
|
||||
- Tutorial: Open a project from a repo: https://docs.microsoft.com/en-us/visualstudio/get-started/tutorial-open-project-from-repo-visual-studio-2019
|
||||
- Comparison of Visual Studio Code and Visual Studio: https://www.itworld.com/article/3403683/visual-studio-code-stepping-on-visual-studios-toes.html
|
||||
|
||||
## Visual Studio Code
|
||||
- Download Visual Studio Code: https://code.visualstudio.com/
|
||||
- Set up Visual Studio Code: https://code.visualstudio.com/docs/setup/setup-overview
|
||||
- Visual Studio Code key bindings and shortcuts: https://code.visualstudio.com/docs/getstarted/keybindings
|
||||
- Windows: https://code.visualstudio.com/shortcuts/keyboard-shortcuts-windows.pdf
|
||||
- macOS: https://code.visualstudio.com/shortcuts/keyboard-shortcuts-macos.pdf
|
||||
- Linux: https://code.visualstudio.com/shortcuts/keyboard-shortcuts-linux.pdf
|
||||
- Latest versions of Visual Studio Code: https://code.visualstudio.com/updates
|
||||
- Visual Studio Code documentation: https://code.visualstudio.com/docs
|
||||
- Visual Studio Code user interface: https://code.visualstudio.com/docs/getstarted/userinterface
|
||||
- Visual Studio Code support for C#: https://code.visualstudio.com/docs/languages/csharp
|
||||
- Source code version control with Visual Studio Code: https://code.visualstudio.com/Docs/editor/versioncontrol
|
||||
- Microsoft's plans for Visual Studio Code: https://github.com/Microsoft/vscode/wiki/Roadmap
|
||||
- Learning with VS Code on Chromebooks: https://code.visualstudio.com/blogs/2020/12/03/chromebook-get-started
|
||||
- Google and Amazon are supporters of Visual Studio Code: https://www.cnbc.com/2018/12/20/microsoft-cmo-capossela-says-google-employees-use-visual-studio-code.html
|
||||
- Visual Studio Code: How Microsoft's 'any OS, any programming language, any software' plan is paying off: https://www.zdnet.com/article/visual-studio-code-
|
||||
how-microsofts-any-os-any-programming-language-any-software-plan-is-paying-off/
|
||||
- The History of Visual Studio Code: https://mybuild.microsoft.com/sessions/6b571733-8198-48da-b870-ef804dcfea93?source=sessions
|
||||
|
||||
## Other C# code editors and platforms
|
||||
- Stack Overflow survey 2021 - Integrated development environment: https://insights.stackoverflow.com/survey/2021#section-most-popular-technologies-integrated-development-environment
|
||||
- Stack Overflow survey 2019 - Most Popular Development Environments: https://insights.stackoverflow.com/survey/2019#development-environments-and-tools
|
||||
- Visual Studio for Mac documentation: https://docs.microsoft.com/en-us/visualstudio/mac/
|
||||
- GitHub Codespaces: https://docs.github.com/en/github/developing-online-with-codespaces/about-codespaces
|
||||
- JetBrains Rider: https://www.jetbrains.com/rider/
|
||||
- Rider documentation: https://www.jetbrains.com/help/rider/Introduction.html
|
||||
- How to code with C# and .NET using a Raspberry Pi 400 with Ubuntu Desktop 64-bit by reading an extra article that I wrote at the following link: https://github.com/markjprice/cs9dotnet5-extras/blob/main/raspberry-pi-ubuntu64/README.md
|
||||
|
||||
## .NET Interactive
|
||||
- .NET Interactive documentation: https://github.com/dotnet/interactive/tree/main/docs
|
||||
- VS Code Notebooks: A Deep Dive video: https://www.youtube.com/watch?v=D-AXZZDTQhM
|
||||
- Discuss: File format in the .NET Interactive VS Code extension: https://github.com/dotnet/interactive/issues/467
|
||||
- Polyglot Notebooks: Variable Sharing: https://devblogs.microsoft.com/dotnet/net-interactive-preview-3-vs-code-insiders-and-polyglot-notebooks/
|
||||
- Using HTML and JavaScript in .NET Interactive: https://github.com/dotnet/interactive/blob/main/docs/javascript-overview.md
|
||||
- .NET Interactive Samples: https://github.com/dotnet/interactive/tree/main/samples
|
||||
- Markdown syntax: https://daringfireball.net/projects/markdown/syntax
|
||||
|
||||
## Command Line Interfaces
|
||||
- Windows Terminal as your Default Command Line Experience: https://devblogs.microsoft.com/commandline/windows-terminal-as-your-default-command-line-experience/
|
||||
- .NET Core Command-Line Interface (CLI) tool: https://aka.ms/dotnet-cli-docs
|
||||
|
||||
## .NET
|
||||
- Download .NET SDK: https://www.microsoft.com/net/download
|
||||
- Stack Overflow survey 2021 - Most loved frameworks and libraries: https://insights.stackoverflow.com/survey/2021#section-most-loved-dreaded-and-wanted-other-frameworks-and-libraries
|
||||
- Themes of .NET: https://themesof.net/
|
||||
- Microsoft's positioning of .NET Core and .NET Framework: https://devblogs.microsoft.com/dotnet/update-on-net-core-3-0-and-net-framework-4-8/
|
||||
- Microsoft's plans for the journey to one .NET: https://devblogs.microsoft.com/dotnet/announcing-net-5-preview-4-and-our-journey-to-one-net/
|
||||
- Official list of .NET 6 supported operating systems: https://github.com/dotnet/core/blob/master/release-notes/6.0/6.0-supported-os.md
|
||||
- Windows ARM64 support: https://github.com/dotnet/runtime/issues/36699
|
||||
- .NET Support Policy: https://dotnet.microsoft.com/platform/support/policy/dotnet-core
|
||||
- .NET versions: https://docs.microsoft.com/en-us/dotnet/core/versions/
|
||||
- .NET Uninstall Tool: https://docs.microsoft.com/en-us/dotnet/core/additional-tools/uninstall-tool
|
||||
- Remove .NET SDKs and runtimes: https://docs.microsoft.com/en-us/dotnet/core/install/remove-runtime-sdk-versions
|
||||
- ASP.NET Core and EF Core branding for .NET 5: https://docs.microsoft.com/en-us/dotnet/core/dotnet-five
|
||||
- .NET Optional SDK Workloads: https://github.com/dotnet/designs/blob/main/accepted/2020/workloads/workloads.md
|
||||
- .NET Core runtime, CoreCLR: https://github.com/dotnet/runtime
|
||||
- .NET Core Roadmap: https://github.com/dotnet/core/blob/master/roadmap.md
|
||||
- .NET Standard FAQ: https://github.com/dotnet/standard/blob/master/docs/faq.md
|
||||
- .NET Standard versions and which .NET platforms support them: https://github.com/dotnet/standard/blob/master/docs/versions.md
|
||||
- .NET Team Members: https://twitter.com/i/lists/120961876//members
|
||||
|
||||
## Open source and other projects related to .NET
|
||||
- Mono project: http://www.mono-project.com/
|
||||
- Unity project: https://docs.unity3d.com/
|
||||
- Open source projects to enable WCF and WF to migrate to .NET 5: https://devblogs.microsoft.com/dotnet/supporting-the-community-with-wf-and-wcf-oss-projects/
|
||||
- Open source project for Blazor Web Forms components: https://github.com/FritzAndFriends/BlazorWebFormsComponents
|
||||
|
||||
## Git
|
||||
- Download Git: https://git-scm.com/download
|
||||
- Download GitHub Desktop: https://desktop.github.com
|
||||
- Become a master at Git and Open Source: https://devblogs.microsoft.com/visualstudio/become-a-master-at-git-and-open-source/
|
||||
|
||||
## Help and learning
|
||||
- Raise an issue with the book: https://github.com/markjprice/cs10dotnet6/issues
|
||||
- .NET Conf 2021 Recap – Videos, Slides, Demos, and More: https://devblogs.microsoft.com/dotnet/net-conf-2021-recap-videos-slides-demos-and-more/
|
||||
- Microsoft Docs: https://docs.microsoft.com/
|
||||
- Official .NET Blog written by the .NET engineering teams: https://devblogs.microsoft.com/dotnet/
|
||||
- Scott Hanselman's videos: http://computerstufftheydidntteachyou.com/
|
||||
- Get better at programming by learning how things work: https://jvns.ca/blog/learn-how-things-work/
|
||||
- Practice .NET anywhere with .NET Fiddle: https://dotnetfiddle.net/
|
||||
- Try .NET: https://try.dot.net/
|
||||
- Stack Overflow: https://stackoverflow.com/
|
||||
- Google Advanced Search: https://www.google.com/advanced_search
|
||||
- Microsoft Learn: https://docs.microsoft.com/en-us/learn/
|
||||
- .NET Videos: https://dotnet.microsoft.com/learn/videos
|
||||
- Microsoft Channel 9 – .NET Videos: https://channel9.msdn.com/Search?term=.net&lang-en=true
|
||||
|
||||
# Chapter 2 - Speaking C#
|
||||
|
||||
## C# language version and the journey to C# 10
|
||||
- Welcome to C# 10: https://devblogs.microsoft.com/dotnet/welcome-to-csharp-10/
|
||||
- Announcing Open Source C# standardization: https://devblogs.microsoft.com/dotnet/announcing-open-source-c-standardization-standards/
|
||||
- Rosalyn and C# compiler versions: https://github.com/dotnet/roslyn/blob/master/docs/wiki/NuGet-packages.md
|
||||
- How Microsoft rewrote its C# compiler in C# and made it open source: https://medium.com/microsoft-open-source-stories/how-microsoft-rewrote-its-c-compiler-in-c-and-made-it-open-source-4ebed5646f98
|
||||
- Current status of the C# language: https://github.com/dotnet/roslyn/blob/master/docs/Language%20Feature%20Status.md
|
||||
- C# Language Specification 5.0: https://www.microsoft.com/en-us/download/details.aspx?id=7029
|
||||
- Draft proposals for C# Language Specifications for 6.0 and later: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/
|
||||
- What's new in C# 7.3: https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7-3
|
||||
- New C# 9.0 features: https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9
|
||||
- C# 9 Workshop: https://github.com/dotnet-presentations/csharp-workshop
|
||||
- C# language versioning: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/configure-language-version
|
||||
- The history of C#: https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history
|
||||
- Interview with the C# Boss - Mads Torgersen: https://www.dotnetcurry.com/csharp/1455/mads-torgersen-interview
|
||||
- The .NET Docs Show - C# Ask Me Anything 🤯: https://www.youtube.com/watch?v=U6cwOzUqjxY
|
||||
- [EPIC] .NET 6 C# project templates use latest language idioms #3359: https://github.com/dotnet/templating/issues/3359
|
||||
- SDK support for implicit namespaces in C# projects #25066: https://github.com/dotnet/docs/issues/25066
|
||||
|
||||
## C# language
|
||||
- C# Reference: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/
|
||||
- C# Programming Guide: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/
|
||||
- C# Keywords: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/index
|
||||
- Naming guidelines: https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/naming-guidelines
|
||||
- Types (C# Programming Guide): https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/types/
|
||||
- Statements, Expressions, and Operators (C# Programming Guide): https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/
|
||||
|
||||
## C# implemented proposals
|
||||
- C# 10: https://github.com/dotnet/csharplang/tree/main/proposals/csharp-10.0
|
||||
- C# 9: https://github.com/dotnet/csharplang/tree/main/proposals/csharp-9.0
|
||||
- C# 8: https://github.com/dotnet/csharplang/tree/main/proposals/csharp-8.0
|
||||
- C# 7.3: https://github.com/dotnet/csharplang/tree/main/proposals/csharp-7.3
|
||||
- C# 7.2: https://github.com/dotnet/csharplang/tree/main/proposals/csharp-7.2
|
||||
- C# 7.1: https://github.com/dotnet/csharplang/tree/main/proposals/csharp-7.1
|
||||
- C# 7.0: https://github.com/dotnet/csharplang/tree/main/proposals/csharp-7.0
|
||||
- C# 6.0: https://github.com/dotnet/csharplang/tree/main/proposals/csharp-6.0
|
||||
|
||||
## C# proposals being worked on
|
||||
- [Proposal] Required Properties #3630: https://github.com/dotnet/csharplang/issues/3630
|
||||
- Champion: Simplified parameter null validation code #2145: https://github.com/dotnet/csharplang/issues/2145
|
||||
- Proposal: Semi-Auto-Properties; field keyword #140: https://github.com/dotnet/csharplang/issues/140
|
||||
- Working Set (of proposals): https://github.com/dotnet/csharplang/milestone/19
|
||||
|
||||
## Text types
|
||||
- Strings (C# Programming Guide): https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/strings/
|
||||
- Verbatim strings: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/verbatim
|
||||
- String Interpolation in C# 10 and .NET 6: https://devblogs.microsoft.com/dotnet/string-interpolation-in-c-10-and-net-6/
|
||||
- Escape sequences: https://devblogs.microsoft.com/csharpfaq/what-character-escape-sequences-are-available/
|
||||
|
||||
## Number types
|
||||
- Floating point numbers: https://ciechanow.ski/exposing-floating-point/
|
||||
- Why 0.1 does not exist in floating-point numbers: https://www.exploringbinary.com/why-0-point-1-does-not-exist-in-floating-point/
|
||||
- American Patriot missile disaster: https://www.ima.umn.edu/~arnold/disasters/patriot.html
|
||||
|
||||
## Console apps and formatting
|
||||
- Main() and command-line arguments (C# Programming Guide): https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/main-and-command-args/
|
||||
- Console Class: https://docs.microsoft.com/en-us/dotnet/api/system.console
|
||||
- Formatting types: https://docs.microsoft.com/en-us/dotnet/standard/base-types/formatting-types
|
||||
- Composite Formatting: https://docs.microsoft.com/en-us/dotnet/standard/base-types/composite-formatting
|
||||
|
||||
## Miscellaneous
|
||||
- An Arabic programming language: https://youtu.be/dkO8cdwf6v8
|
||||
|
||||
# Chapter 3 - Controlling Flow and Converting Types
|
||||
|
||||
## Operators
|
||||
- C# operators: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/operators
|
||||
- Truth tables: https://en.wikipedia.org/wiki/Truth_table
|
||||
- Side effects: https://en.wikipedia.org/wiki/Side_effect_(computer_science)
|
||||
- Bitwise and shift operators: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/bitwise-and-shift-operators
|
||||
- Member access operators: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/member-access-operators
|
||||
|
||||
## Branching statements and pattern matching
|
||||
- Statement keywords (C# Reference): https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/statement-keywords
|
||||
- Goto Fail bug: https://gotofail.com/
|
||||
- goto keyword and examples of when it can be used: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/goto
|
||||
- Pattern matching: https://docs.microsoft.com/en-us/dotnet/csharp/pattern-matching
|
||||
- Patterns and switch expressions: https://devblogs.microsoft.com/dotnet/do-more-with-patterns-in-c-8-0/
|
||||
|
||||
## Casting, converting, rounding, and formatting data
|
||||
- Taking control of rounding: https://docs.microsoft.com/en-us/dotnet/api/system.math.round
|
||||
- Format codes for common scenarios: https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings
|
||||
- Casting and type conversions (C# Programming Guide): https://docs.microsoft.com/en-us/dotnet/articles/csharp/programming-guide/types/casting-and-type-conversions
|
||||
|
||||
## Handling exceptions
|
||||
- 10 Exception handling best practices in C#: https://kumarashwinhubert.com/10-exception-handling-best-practices-in-csharp
|
||||
|
||||
## Coding problems in interviews
|
||||
- Why can't programmers program? http://blog.codinghorror.com/why-cant-programmers-program/
|
||||
- FizzBuzz for programming interviews: http://imranontech.com/2007/01/24/using-fizzbuzz-to-find-developers-who-grok-coding/
|
||||
- Make Better Hiring Decisions With Take-Home Coding Challenges: https://codesubmit.io
|
||||
- The 30-minute guide to rocking your next coding interview: https://www.freecodecamp.org/news/coding-interviews-for-dummies-5e048933b82b/
|
||||
- C# Interview Questions: https://www.codingdojo.com/blog/c-interview-questions
|
||||
|
||||
# Chapter 4 - Writing, Debugging, and Testing Functions
|
||||
|
||||
## Concepts
|
||||
- Recursion versus interation: https://en.wikipedia.org/wiki/Recursion_(computer_science)#Recursion_versus_iteration
|
||||
|
||||
## Debugging
|
||||
- Visual Studio for Windows debugger documentation: https://docs.microsoft.com/en-us/visualstudio/debugger/
|
||||
- Visual Studio Code debugger: https://code.visualstudio.com/docs/editor/debugging
|
||||
- Instructions for setting up the .NET debugger: https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger.md
|
||||
- How to debug for absolute beginners: https://docs.microsoft.com/en-us/visualstudio/debugger/debugging-absolute-beginners
|
||||
|
||||
## Instrumenting
|
||||
- System.Diagnostics Namespace: https://docs.microsoft.com/en-us/dotnet/core/api/system.diagnostics
|
||||
- Debug class: https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.debug
|
||||
- Trace listeners: https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.tracelistener
|
||||
- Console log formatting: https://docs.microsoft.com/en-us/dotnet/core/extensions/console-log-formatter
|
||||
- Using .env in .NET: https://dusted.codes/dotenv-in-dotnet
|
||||
- Announcing dotnet monitor in .NET 6: https://devblogs.microsoft.com/dotnet/announcing-dotnet-monitor-in-net-6/
|
||||
|
||||
## Testing
|
||||
- Test Driven Development (TDD): https://en.wikipedia.org/wiki/Test-driven_development
|
||||
- Unit testing in .NET: https://docs.microsoft.com/en-us/dotnet/core/testing/
|
||||
- xUnit.net: http://xunit.github.io/
|
||||
|
||||
# Chapter 5 - Building Your Own Types with Object-Oriented Programming
|
||||
|
||||
## Fields
|
||||
- Fields (C# programming guide): https://docs.microsoft.com/en-us/dotnet/articles/csharp/programming-guide/classes-and-structs/fields
|
||||
- Access modifiers (C# programming guide): https://docs.microsoft.com/en-us/dotnet/articles/csharp/language-reference/keywords/access-modifiers
|
||||
- Enumeration types (C# reference): https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/enum
|
||||
|
||||
## Methods
|
||||
- Constructors (C# programming guide): https://docs.microsoft.com/en-us/dotnet/articles/csharp/programming-guide/classes-and-structs/constructors
|
||||
- Methods (C# programming guide): https://docs.microsoft.com/en-us/dotnet/articles/csharp/methods
|
||||
- Deconstruct method: https://docs.microsoft.com/en-us/dotnet/csharp/deconstruct
|
||||
- params keyword: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/params
|
||||
- ref keyword: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/ref-returns
|
||||
|
||||
## Properties
|
||||
- Encapsulation of fields using properties: https://stackoverflow.com/questions/1568091/why-use-getters-and-setters-accessors
|
||||
- Properties (C# programming guide): https://docs.microsoft.com/en-us/dotnet/articles/csharp/properties
|
||||
- Using Tuples in C# to Initialize Properties in the Constructor and to Deconstruct Your Object: https://www.thomasclaudiushuber.com/2021/03/25/csharp-using-tuples-to-initialize-properties/
|
||||
|
||||
## Pattern matching
|
||||
- Pattern matching tutorial: https://docs.microsoft.com/en-us/dotnet/csharp/tutorials/pattern-matching
|
||||
|
||||
## Records
|
||||
- Comparing struct and class records: https://devblogs.microsoft.com/dotnet/announcing-net-6-release-candidate-2/#record-structs
|
||||
|
||||
## Miscellaneous
|
||||
- How to calculate someone's age: https://stackoverflow.com/questions/9/how-do-i-calculate-someones-age-in-c
|
||||
|
||||
# Chapter 6 - Implementing Interfaces and Inheriting Classes
|
||||
|
||||
## Operator overloading
|
||||
- Operator (C# reference): https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/operator-overloading
|
||||
- Symbols that can be overloaded: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/overloadable-operators
|
||||
|
||||
## Delegates and events
|
||||
- Delegates: https://docs.microsoft.com/en-us/dotnet/standard/delegates-lambdas
|
||||
- Events (C# reference): https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/event
|
||||
- Custom EventArgs: https://docs.microsoft.com/en-us/dotnet/standard/events/how-to-raise-and-consume-events
|
||||
|
||||
## Interfaces and generics
|
||||
- Interfaces: https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/types/interfaces
|
||||
- Explicit interface implementations: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/interfaces/explicit-interface-implementation
|
||||
- Design decisions for default interface implementations: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/default-interface-methods
|
||||
- Default interface implementations tutorial: https://docs.microsoft.com/en-us/dotnet/csharp/tutorials/default-interface-members-versions
|
||||
- Generics (C# Programming Guide): https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics
|
||||
|
||||
## Memory, reference and value types
|
||||
- Reference Types (C# Reference): https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/reference-types
|
||||
- Value Types (C# reference): https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/value-types
|
||||
- Choosing Between Class and Struct: https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/choosing-between-class-and-struct
|
||||
- Conversation about the .NET type system: https://devblogs.microsoft.com/dotnet/conversation-about-the-net-type-system/
|
||||
- Technical details of the internal memory layout of types in .NET: https://adamsitnik.com/Value-Types-vs-Reference-Types/
|
||||
- Garbage collection: https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/
|
||||
- Finalizers (C# Programming Guide): https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/destructors
|
||||
- Finalizers and disposing: https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/unmanaged
|
||||
- IDisposable interface: https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/using-objects
|
||||
|
||||
## Null and nullability
|
||||
- The inventor of null, Sir Charles Antony Richard Hoare, admits his mistake in a recorded hour-long talk: https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare
|
||||
- The tweet about achieving 80% annotations in .NET 5: https://twitter.com/terrajobst/status/1296566363880742917
|
||||
- Video to learn how to get rid of null reference exceptions forever: https://channel9.msdn.com/Shows/On-NET/This-is-how-you-get-rid-of-null-reference-exceptions-forever
|
||||
- Nullable value types (C# reference): https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/nullable-value-types
|
||||
- Nullable reference types: https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references
|
||||
- Null-conditional operator: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-conditional-operators
|
||||
- Null-coalescing operator: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-coalescing-operator
|
||||
|
||||
## Inheritance
|
||||
- Inheritance (C# Programming Guide): https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/inheritance
|
||||
|
||||
# Chapter 7 - Packaging and Distributing .NET Types
|
||||
|
||||
## .NET Standard
|
||||
- .NET Standard 2.1 announcement: https://devblogs.microsoft.com/dotnet/announcing-net-standard-2-1/
|
||||
- .NET Standard 2.1 APIs and a comparison with .NET Standard 2.0: https://github.com/dotnet/standard/blob/master/docs/versions/netstandard2.1.md
|
||||
- Future of .NET Standard: https://devblogs.microsoft.com/dotnet/the-future-of-net-standard/
|
||||
- From .NET Standard to .NET 5: https://www.codemag.com/article/2010022
|
||||
|
||||
## .NET versions
|
||||
- .NET Core 1.0 announcement: https://devblogs.microsoft.com/dotnet/announcing-net-core-1-0/
|
||||
- .NET Core 1.1 announcement: https://devblogs.microsoft.com/dotnet/announcing-net-core-1-1/
|
||||
- .NET Core 2.0 announcement: https://devblogs.microsoft.com/dotnet/announcing-net-core-2-0/
|
||||
- .NET Core 2.2 announcement: https://devblogs.microsoft.com/dotnet/announcing-net-core-2-2/
|
||||
- .NET Core 3.0 announcement: https://devblogs.microsoft.com/dotnet/announcing-net-core-3-0/
|
||||
- .NET Core 3.1 announcement: https://devblogs.microsoft.com/dotnet/announcing-net-core-3-1/
|
||||
- .NET 5 announcement: https://devblogs.microsoft.com/dotnet/announcing-net-5-0
|
||||
- Performance improvements in .NET 5: https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-5/
|
||||
- .NET 5.0 Runtime Highlights: https://codemag.com/Article/2010092/.NET-5.0-Runtime-Highlights
|
||||
- Announcing .NET 6 — The Fastest .NET Yet: https://devblogs.microsoft.com/dotnet/announcing-net-6/
|
||||
|
||||
## .NET API, packages and frameworks
|
||||
- Search and browse all .NET APIs: https://docs.microsoft.com/en-us/dotnet/api/
|
||||
- CoreFX README.md: https://github.com/dotnet/corefx/blob/master/Documentation/README.md
|
||||
- .NET 6.0 TFMs: https://github.com/dotnet/designs/pull/174/files
|
||||
- How packages and their APIs relate to frameworks: https://gist.github.com/davidfowl/8939f305567e1755412d6dc0b8baf1b7
|
||||
- Example .NET package and the frameworks it supports: https://www.nuget.org/packages/System.IO.FileSystem/
|
||||
- Currently supported Runtime Identifier (RID) values: https://docs.microsoft.com/en-us/dotnet/articles/core/rid-catalog
|
||||
- System.IO.FileSystem package details: https://www.nuget.org/packages/System.IO.FileSystem/
|
||||
|
||||
## Roslyn, SDKs, and project templates
|
||||
- Roslyn compiler: https://github.com/dotnet/roslyn
|
||||
- .NET project SDKs: https://docs.microsoft.com/en-us/dotnet/core/project-sdk/overview
|
||||
- global.json overview: https://docs.microsoft.com/en-us/dotnet/core/tools/global-json
|
||||
- Additional `dotnet new` templates: https://dotnetnew.azurewebsites.net/
|
||||
- Tutorial: Create an item template: https://docs.microsoft.com/en-us/dotnet/core/tutorials/cli-templates-create-item-template
|
||||
|
||||
## Packaging and publishing
|
||||
- PDB files: https://www.wintellect.com/pdb-files-what-every-developer-must-know/
|
||||
- Single-file app issue: https://github.com/dotnet/runtime/issues/36590
|
||||
- App trimming: https://devblogs.microsoft.com/dotnet/app-trimming-in-net-5/
|
||||
- The PackageReference format: https://docs.microsoft.com/en-us/nuget/reference/msbuild-targets
|
||||
- NuGet packages: https://www.nuget.org/packages
|
||||
- Announcing NuGet 6.0: https://devblogs.microsoft.com/nuget/announcing-nuget-6/
|
||||
- .NET Core application publishing overview: https://docs.microsoft.com/en-us/dotnet/core/deploying/
|
||||
- How to Deploy .NET apps to Raspberry Pi: https://www.ryadel.com/en/deploy-net-apps-raspberry-pi/
|
||||
- How to solve a circular reference using an interface: https://stackoverflow.com/questions/6928387/how-to-solve-circular-reference
|
||||
- Semantic versioning: http://semver.org
|
||||
|
||||
## Decompiling .NET
|
||||
- Is it possible to “decompile” a Windows .exe? Or at least view the Assembly? https://stackoverflow.com/questions/273145/is-it-possible-to-decompile-a-windows-exe-or-at-least-view-the-assembly
|
||||
- IDA Freeware Download Page: https://www.hex-rays.com/products/ida/support/download_freeware/
|
||||
|
||||
## Porting from .NET Framework to modern .NET
|
||||
- Overview of porting from .NET Framework to .NET Core: https://docs.microsoft.com/en-us/dotnet/core/porting/
|
||||
- What .NET Developers ought to know: https://www.hanselman.com/blog/WhatNETDevelopersOughtToKnowToStartIn2017.aspx
|
||||
- Introducing the .NET Upgrade Assistant Preview: https://devblogs.microsoft.com/dotnet/introducing-the-net-upgrade-assistant-preview/
|
||||
- .NET Upgrade Assistant: https://dotnet.microsoft.com/platform/upgrade-assistant
|
||||
- Discover cross-platform issues: https://docs.microsoft.com/en-us/dotnet/standard/analyzers/api-analyzer#discover-cross-platform-issues
|
||||
- Amazon Porting Assistant for .NET: https://aws.amazon.com/porting-assistant-dotnet/
|
||||
|
||||
## Preview features
|
||||
- Preview Features: https://github.com/dotnet/designs/blob/main/accepted/2021/preview-features/preview-features.md
|
||||
|
||||
# Chapter 8 - Working with Common .NET Types
|
||||
|
||||
## Working with numbers
|
||||
- Numerics types: https://docs.microsoft.com/en-us/dotnet/standard/numerics
|
||||
- The Half type: https://devblogs.microsoft.com/dotnet/introducing-the-half-type/
|
||||
- nint and nuint types (C# reference): https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/nint-nuint
|
||||
- nint Struct: https://docs.microsoft.com/en-us/dotnet/api/system.nint
|
||||
- Names for large numbers: https://en.wikipedia.org/wiki/Names_of_large_numbers
|
||||
|
||||
## Working with text and regular expressions
|
||||
- String Class: https://docs.microsoft.com/en-us/dotnet/api/system.string
|
||||
- Regex Class: https://docs.microsoft.com/en-us/dotnet/api/system.text.regularexpressions.regex
|
||||
- Regular expressions in .NET: https://docs.microsoft.com/en-us/dotnet/articles/standard/base-types/regular-expressions
|
||||
- Regular Expression Language – Quick Reference: https://docs.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-language-quick-reference
|
||||
- Unicode in regular expressions: https://www.regular-expressions.info/unicode.html
|
||||
- Using a regular expression to split a comma-separated string: https://stackoverflow.com/questions/18144431/regex-to-split-a-csv
|
||||
- To specify a Unicode character, use `\u` followed by four characters specifying the number of the character. For example, `\u00c0` is the À character: https://www.regular-expressions.info
|
||||
- Performance improvements in .NET 5: https://devblogs.microsoft.com/dotnet/regex-performance-improvements-in-net-5/
|
||||
|
||||
## Dates and times
|
||||
- Date, Time, and Time Zone Enhancements in .NET 6: https://devblogs.microsoft.com/dotnet/date-time-and-time-zone-enhancements-in-net-6/
|
||||
- .NET 6: Date and Time Structures: https://www.infoq.com/news/2021/04/Net6-Date-Time/
|
||||
- Noda Time: A better date and time API for .NET: https://nodatime.org/
|
||||
|
||||
## Collections
|
||||
- Collections (C# and Visual Basic): https://docs.microsoft.com/en-us/dotnet/api/system.collections
|
||||
- Collections: https://docs.microsoft.com/en-us/dotnet/standard/collections
|
||||
- Concurrent collections: https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent
|
||||
- What types should I use to pass collections in C#? https://markheath.net/post/passing-collections-csharp
|
||||
- On Abstractions and For-Each Performance in C#: https://www.infoq.com/articles/For-Each-Performance/
|
||||
|
||||
## Spans
|
||||
- Span<T>: https://docs.microsoft.com/en-us/dotnet/api/system.span-1
|
||||
- How spans work internally: https://docs.microsoft.com/en-us/archive/msdn-magazine/2018/january/csharp-all-about-span-exploring-a-new-net-mainstay
|
||||
|
||||
## Networking
|
||||
- .NET 6 Networking Improvements: https://devblogs.microsoft.com/dotnet/dotnet-6-networking-improvements/
|
||||
|
||||
## Assemblies and reflection
|
||||
- .NET API Reference: https://docs.microsoft.com/en-us/dotnet/api/
|
||||
- Compiler-generated display class: http://stackoverflow.com/a/2509524/55847
|
||||
- Dynamically load assemblies that are not currently referenced: https://docs.microsoft.com/en-us/dotnet/standard/assembly/unloadability-howto
|
||||
- Dynamically execute code: https://docs.microsoft.com/en-us/dotnet/api/system.reflection.methodbase.invoke
|
||||
- Dynamically generate new code and assemblies: https://docs.microsoft.com/en-us/dotnet/api/system.reflection.emit.assemblybuilder
|
||||
- Extending Metadata Using Attributes: https://docs.microsoft.com/en-us/dotnet/standard/attributes/
|
||||
|
||||
## Working with images
|
||||
- ImageSharp: https://github.com/SixLabors/ImageSharp
|
||||
|
||||
## Internationalization
|
||||
- Globalizing and localizing .NET applications: https://docs.microsoft.com/en-us/dotnet/standard/globalization-localization/
|
||||
- Time zones: https://devblogs.microsoft.com/dotnet/cross-platform-time-zones-with-net-core/
|
||||
|
||||
# Chapter 9 - Working with Files, Streams, and Serialization
|
||||
|
||||
## File system, encoding, and streams
|
||||
- File System and the Registry (C# Programming Guide): https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/file-system/
|
||||
- Character encoding in .NET: https://docs.microsoft.com/en-us/dotnet/articles/standard/base-types/character-encoding
|
||||
- Async streams tutorial: https://docs.microsoft.com/en-us/dotnet/csharp/tutorials/generate-consume-asynchronous-stream
|
||||
- How pipelines help with I/O performance: https://devblogs.microsoft.com/dotnet/system-io-pipelines-high-performance-io-in-net/
|
||||
|
||||
## Serialization
|
||||
- Serialization (C#): https://docs.microsoft.com/en-us/dotnet/articles/csharp/programming-guide/concepts/serialization/
|
||||
- Serializing to Files, TextWriters, and XmlWriters: https://docs.microsoft.com/en-us/dotnet/standard/linq/serialize-files-textwriters-xmlwriters
|
||||
|
||||
## JSON
|
||||
- Newtonsoft Json.NET: https://www.newtonsoft.com/json
|
||||
- System.Text.Json APIs: https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-apis/
|
||||
- Issues solved by the new JSON APIs, including JamesNK's comments: https://github.com/dotnet/corefx/issues/33115
|
||||
- How to migrate from Newtonsoft.Json to System.Text.Json: https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to
|
||||
- What’s next for System.Text.Json? https://devblogs.microsoft.com/dotnet/whats-next-for-system-text-json/
|
||||
|
||||
# Chapter 10 - Working with Data Using Entity Framework Core
|
||||
|
||||
## Legacy EF 6
|
||||
- Entity Framework 6.3 and its .NET Core 3.0 and later support: https://devblogs.microsoft.com/dotnet/announcing-ef-core-3-0-and-ef-6-3-general-availability/
|
||||
|
||||
## EF Core
|
||||
- EF Core documentation: https://docs.microsoft.com/en-us/ef/core/
|
||||
- EF Core team's plans: https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-5.0/plan
|
||||
- New features in EF Core 5: https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-5.0/whatsnew
|
||||
- New features in EF Core 6: https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-6.0/whatsnew
|
||||
- Get to Know EF Core 6: https://devblogs.microsoft.com/dotnet/get-to-know-ef-core-6/
|
||||
- Announcing the Plan for EF7: https://devblogs.microsoft.com/dotnet/announcing-the-plan-for-ef7/
|
||||
- Plans for Entity Framework Core 6.0 revealed as Microsoft admits it is unlikely to match Dapper for performance: https://www.theregister.com/2021/01/19/entity_framework_core_6/
|
||||
- Entity Framework Community Standup - Performance Tuning an EF Core App: https://www.youtube.com/watch?v=VgNFFEqwZPU
|
||||
|
||||
## EF Core database providers
|
||||
- EF Core database providers: https://docs.microsoft.com/en-us/ef/core/providers/
|
||||
- Devart database providers: https://www.devart.com/dotconnect/entityframework.html
|
||||
- Check the latest NuGet package version: https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Sqlite/
|
||||
- Document Database Providers for Entity Framework Core: https://github.com/BlueshiftSoftware/EntityFrameworkCore
|
||||
|
||||
## SQLite
|
||||
- Download SQLite: https://www.sqlite.org/download.html
|
||||
- SQL statements supported by SQLite: https://sqlite.org/lang.html
|
||||
- Download SQLiteStudio: http://sqlitestudio.pl
|
||||
|
||||
## Microsoft SQL Server
|
||||
- Try SQL Server on-premises or in the cloud: https://www.microsoft.com/en-us/sql-server/sql-server-downloads
|
||||
- Download SQL Server Management Studio (SSMS): https://docs.microsoft.com/en-us/sql/ssms/download-sql-server-management-studio-ssms
|
||||
- Quickstart: Connect and query a SQL Server instance using SQL Server Management Studio (SSMS): https://docs.microsoft.com/en-us/sql/ssms/quickstarts/ssms-connect-query-sql-server
|
||||
- Quickstart: Connect and query an Azure SQL Database or an Azure Managed Instance using SQL Server Management Studio (SSMS): https://docs.microsoft.com/en-us/sql/ssms/quickstarts/ssms-connect-query-azure-sql
|
||||
- Use Visual Studio Code to create and run Transact-SQL scripts: https://docs.microsoft.com/en-us/sql/tools/visual-studio-code/sql-server-develop-use-vscode
|
||||
- .NET Interactive with SQL!| .NET Notebooks in Visual Studio Code: https://devblogs.microsoft.com/dotnet/net-interactive-with-sql-net-notebooks-in-visual-studio-code/
|
||||
|
||||
## NoSQL data stores
|
||||
- Welcome to Azure Cosmos DB: https://docs.microsoft.com/en-us/azure/cosmos-db/introduction
|
||||
- Use NoSQL databases as a persistence infrastructure: https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/microservice-ddd-cqrs-patterns/nosql-database-persistence-infrastructure
|
||||
|
||||
## EF Core models
|
||||
- EF Core model conventions: https://docs.microsoft.com/en-us/ef/core/modeling/
|
||||
- Data seeding: https://docs.microsoft.com/en-us/ef/core/modeling/data-seeding
|
||||
- Humanizer library: http://humanizr.net
|
||||
- Scaffolding: https://docs.microsoft.com/en-us/ef/core/managing-schemas/scaffolding?tabs=dotnet-core-cli
|
||||
- Deep Dive into Many-to-Many: A Tour of EF Core 5.0: https://channel9.msdn.com/Shows/On-NET/Deep-Dive-into-Many-to-Many-A-Tour-of-EF-Core-50-pt-2
|
||||
- Naming Conventions for Entity Framework Core Tables and Columns: https://github.com/efcore/EFCore.NamingConventions
|
||||
|
||||
## EF Core querying and manipulating
|
||||
- Filtered include: https://docs.microsoft.com/en-us/ef/core/querying/related-data/eager#filtered-include
|
||||
- Query tags: https://docs.microsoft.com/en-us/ef/core/querying/tags
|
||||
- Loading patterns: https://docs.microsoft.com/en-us/ef/core/querying/related-data
|
||||
- Pooling database contexts: https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-2.0#dbcontext-pooling
|
||||
|
||||
# Chapter 11 - Querying and Manipulating Data Using LINQ
|
||||
|
||||
## LINQ concepts
|
||||
- Expression trees: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expression-trees/
|
||||
- LINQ comprehension syntax: https://stackoverflow.com/questions/6229187/linq-why-is-it-called-comprehension-syntax
|
||||
- LINQ in C#: https://docs.microsoft.com/en-us/dotnet/csharp/linq/linq-in-csharp
|
||||
- New LINQ extensions in .NET 6 and benchmarks: https://blog.elmah.io/new-linq-extensions-in-net-6-and-benchmarks/
|
||||
|
||||
## LINQ providers
|
||||
- EF Core LINQ queries no longer evaluated at client: https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.x/breaking-changes#linq-queries-are-no-longer-evaluated-on-the-client
|
||||
- Parallel LINQ (PLINQ): https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/introduction-to-plinq
|
||||
- LINQ to XML Overview (C#): https://docs.microsoft.com/en-gb/dotnet/csharp/programming-guide/concepts/linq/linq-to-xml-overview
|
||||
|
||||
## LINQ samples and tools
|
||||
- 101 LINQ Samples: https://docs.microsoft.com/en-us/samples/dotnet/try-samples/101-linq-samples/
|
||||
- LINQPad: https://www.linqpad.net/
|
||||
|
||||
# Chapter 12 - Improving Performance and Scalability Using Multitasking
|
||||
|
||||
## Threads
|
||||
- Threads and threading: https://docs.microsoft.com/en-us/dotnet/standard/threading/threads-and-threading
|
||||
- Thread pool: https://docs.microsoft.com/en-us/dotnet/standard/threading/the-managed-thread-pool
|
||||
|
||||
## Tasks
|
||||
- Pros and cons of different ways to start tasks: https://devblogs.microsoft.com/pfxteam/task-factory-startnew-vs-new-task-start/
|
||||
- Task.Start Method: https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.start
|
||||
|
||||
## Thread safety
|
||||
- Events and thread-safety: https://docs.microsoft.com/en-us/archive/blogs/cburrows/field-like-events-considered-harmful
|
||||
- Stephen Cleary's thoughts on events and thread-safety: https://blog.stephencleary.com/2009/06/threadsafe-events.html
|
||||
- Managed threading best practices: https://docs.microsoft.com/en-us/dotnet/standard/threading/managed-threading-best-practices
|
||||
- ThreadStaticAttribute Class: https://docs.microsoft.com/en-us/dotnet/api/system.threadstaticattribute
|
||||
|
||||
## async and await
|
||||
- Async in depth: https://docs.microsoft.com/en-us/dotnet/standard/async-in-depth
|
||||
- await (C# reference): https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/await
|
||||
- Six Essential Tips for Async: https://channel9.msdn.com/Series/Three-Essential-Tips-for-Async
|
||||
- Exploring the async/await State Machine – The Awaitable Pattern: https://vkontech.com/exploring-the-async-await-state-machine-the-awaitable-pattern/
|
||||
- There Is No Thread: https://blog.stephencleary.com/2013/11/there-is-no-thread.html
|
||||
|
||||
## Parallel programming
|
||||
- Parallel Programming in .NET: https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/
|
||||
|
||||
## Sharing resouces and synchronization
|
||||
- Overview of synchronization primitives: https://docs.microsoft.com/en-us/dotnet/standard/threading/overview-of-synchronization-primitives
|
||||
|
||||
# Chapter 13 - Introducing Practical Applications of C# and .NET
|
||||
|
||||
## .NET apps
|
||||
- .NET Application Architecture Guidance: https://www.microsoft.com/net/learn/architecture
|
||||
|
||||
## .NET Content Management Systems
|
||||
- Piranha CMS: https://piranhacms.org/
|
||||
- Orchard Core: http://www.orchardcore.net/
|
||||
- Optimizely CMS Developer Guide: https://world.optimizely.com/documentation/developer-guides/CMS/
|
||||
- Umbraco CMS: https://umbraco.com/products/umbraco-cms/migrating-umbraco-to-net-core/
|
||||
|
||||
## ASP.NET versions and features
|
||||
- ASP.NET Core 1.0 announcement: https://devblogs.microsoft.com/aspnet/announcing-asp-net-core-1-0/
|
||||
- ASP.NET Core 1.1 announcement: https://devblogs.microsoft.com/aspnet/announcing-asp-net-core-1-1/
|
||||
- ASP.NET Core 2.0 announcement: https://devblogs.microsoft.com/aspnet/announcing-asp-net-core-2-0/
|
||||
- ASP.NET Core 2.1 announcement: https://devblogs.microsoft.com/aspnet/asp-net-core-2-1-0-now-available/
|
||||
- ASP.NET Core 2.2 announcement: https://devblogs.microsoft.com/aspnet/asp-net-core-2-2-available-today/
|
||||
- ASP.NET Core 3.0 announcement: https://devblogs.microsoft.com/aspnet/a-first-look-at-changes-coming-in-asp-net-core-3-0/
|
||||
- ASP.NET Core 3.1 announcement: https://devblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-core-3-1/
|
||||
- Blazor WebAssembly announcement: https://devblogs.microsoft.com/aspnet/blazor-webassembly-3-2-0-now-available/
|
||||
- ASP.NET Core 5.0 announcement: https://devblogs.microsoft.com/aspnet/announcing-asp-net-core-in-net-5/
|
||||
|
||||
## Web development technologies
|
||||
- WebSocket: https://en.wikipedia.org/wiki/WebSocket
|
||||
- MessagePack: https://msgpack.org
|
||||
- WebAssembly: https://webassembly.org
|
||||
- TypeScript: https://www.typescriptlang.org
|
||||
|
||||
## Windows desktop app development
|
||||
- Choices of platform for building Windows desktop apps: https://docs.microsoft.com/en-us/windows/apps/desktop/choose-your-platform
|
||||
- What’s new in Windows Forms in .NET 6.0: https://devblogs.microsoft.com/dotnet/whats-new-in-windows-forms-in-net-6-0/
|
||||
- Migrating WPF apps: https://devblogs.microsoft.com/dotnet/migrating-a-sample-wpf-app-to-net-core-3-part-1/
|
||||
- Windows Compatibility Pack: https://devblogs.microsoft.com/dotnet/announcing-the-windows-compatibility-pack-for-net-core/
|
||||
- Packt book about building WPF apps: https://www.packtpub.com/product/mastering-windows-presentation-foundation/9781785883002
|
||||
- UWP apps: https://docs.microsoft.com/en-us/windows/uwp/get-started/universal-application-platform-guide
|
||||
- Nine desktop development options, from WPF to Blazor: https://visualstudiomagazine.com/articles/2021/03/25/desktop-options.aspx
|
||||
|
||||
## Third-party cross-platform GUI development
|
||||
- avalonia - A cross platform XAML framework for .NET: https://avaloniaui.net
|
||||
- Uno Platform: https://platform.uno
|
||||
- Getting Started with the Uno Platform (Project Reunion/WinUI): https://nicksnettravels.builttoroam.com/getting-started-uno-platform/
|
||||
|
||||
# Chapter 14 - Building Websites Using ASP.NET Core Razor Pages
|
||||
|
||||
## General web development
|
||||
- HTML5 and CSS3: Building Responsive Websites: https://www.packtpub.com/producthtml5-and-css3-building-responsive-websites/9781787124813h
|
||||
- Kestrel web server: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel
|
||||
- Free TLS/SSL certificates: https://letsencrypt.org
|
||||
- ASP.NET Core hosting environments: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/environments
|
||||
- Content Delivery Network (CDN): https://en.wikipedia.org/wiki/Content_delivery_network
|
||||
- Bootstrap: https://getbootstrap.com/
|
||||
- Compact Folders feature: https://github.com/microsoft/vscode-docs/blob/vnext/release-notes/v1_41.md#compact-folders-in-explorer
|
||||
|
||||
## ASP.NET Core
|
||||
- Announcing ASP.NET Core in .NET 6: https://devblogs.microsoft.com/dotnet/announcing-asp-net-core-in-net-6/
|
||||
- ASP.NET Core fundamentals: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/
|
||||
- Static files in ASP.NET Core: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/static-files
|
||||
- HttpContext class: https://docs.microsoft.com/en-us/dotnet/api/system.web.httpcontext
|
||||
|
||||
## Razor Pages and layouts
|
||||
- Introducing the new Razor editor in Visual Studio 2022: https://devblogs.microsoft.com/visualstudio/introducing-the-new-razor-editor-in-visual-studio-2022/
|
||||
- Introduction to Razor Pages in ASP.NET Core: https://docs.microsoft.com/en-us/aspnet/core/razor-pages/
|
||||
- Razor syntax reference for ASP.NET Core: https://docs.microsoft.com/en-us/aspnet/core/mvc/views/razor
|
||||
- Layout in ASP.NET Core: https://docs.microsoft.com/en-us/aspnet/core/mvc/views/layout
|
||||
- Tag Helpers in ASP.NET Core: https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/intro
|
||||
- ASP.NET Core Razor Pages with EF Core: https://docs.microsoft.com/en-us/aspnet/core/data/ef-rp/intro
|
||||
- Registering a database context for use as a dependency service: https://docs.microsoft.com/en-us/ef/core/miscellaneous/configuring-dbcontext#using-dbcontext-with-dependency-injection
|
||||
- The `<partial>` tag helper: https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/built-in/partial-tag-helper
|
||||
|
||||
## Endpoint routing
|
||||
- Simple examples of Run, Map, and Use: https://www.vaughanreid.com/2020/05/using-in-line-middleware-in-asp-net-core/
|
||||
- Automatically visualize your endpoints: https://andrewlock.net/visualizing-asp-net-core-endpoints-using-graphvizonline-and-the-dot-language/
|
||||
- Configuring the HTTP pipeline with middleware: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware
|
||||
- DEEP DIVE: HOW IS THE ASP.NET CORE MIDDLEWARE PIPELINE BUILT? https://www.stevejgordon.co.uk/how-is-the-asp-net-core-middleware-pipeline-built
|
||||
|
||||
# Chapter 15 - Building Websites Using the Model-View-Controller Pattern
|
||||
|
||||
## Security and privacy
|
||||
- Built-in features for compliance with modern privacy requirements like GDPR: https://docs.microsoft.com/en-us/aspnet/core/security/gdpr
|
||||
- ASP.NET Core's support for authenticator apps: https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity-enable-qrcodes
|
||||
- Identity UI library: https://docs.microsoft.com/en-us/aspnet/core/security/authentication/scaffold-identity?tabs=netcore-cli
|
||||
- Authorization: https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction
|
||||
- Anti-forgery tokens: https://docs.microsoft.com/en-us/aspnet/core/security/anti-request-forgery
|
||||
|
||||
## Setting up and configuring
|
||||
- dotnet new templates: https://github.com/dotnet/templating/wiki/Available-templates-for-dotnet-new
|
||||
- Overview of ASP.NET Core MVC: https://docs.microsoft.com/en-us/aspnet/core/mvc/overview
|
||||
- Default configuration of web hosts: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/web-host
|
||||
- Dependency injection for ASP.NET Core: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection
|
||||
- Configuring middleware: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/
|
||||
- Announcing YARP 1.0 Release: https://devblogs.microsoft.com/dotnet/announcing-yarp-1-0-release/
|
||||
|
||||
## Controllers
|
||||
- Handle requests with controllers in ASP.NET Core MVC: https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/actions
|
||||
- Response caching: https://docs.microsoft.com/en-us/aspnet/core/performance/caching/response
|
||||
- How cache busting using query strings works: https://stackoverflow.com/questions/9692665/cache-busting-via-params
|
||||
|
||||
## Models
|
||||
- Model Binding in ASP.NET Core: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/model-binding
|
||||
- Create your own model binders by implementing the IModelBinder interface: https://docs.microsoft.com/en-us/aspnet/core/mvc/advanced/custom-model-binding
|
||||
- Model validation: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation
|
||||
- Tutorial: Get started with EF Core in an ASP.NET MVC web app: https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro
|
||||
|
||||
## Views
|
||||
- HtmlHelper class: https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.viewfeatures.htmlhelper
|
||||
- Views in ASP.NET Core MVC: https://docs.microsoft.com/en-us/aspnet/core/mvc/views/overview
|
||||
- Why it is good to put <script> elements at the bottom of the <body>: https://stackoverflow.com/questions/436411/where-should-i-put-script-tags-in-html-markup
|
||||
|
||||
## Miscellaneous
|
||||
- How to unit test controllers: https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/testing
|
||||
- Filters for cross-concern functionality: https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters
|
||||
- Free stock photos for commercial use with no attribution: https://www.pexels.com/
|
||||
- Images of eight categories: https://github.com/markjprice/cs9dotnet5/tree/master/Assets/Categories
|
||||
|
||||
# Chapter 16 - Building and Consuming Web Services
|
||||
|
||||
## Web service technologies
|
||||
- Media types: http://en.wikipedia.org/wiki/Media_type
|
||||
- WS-* standards: https://en.wikipedia.org/wiki/List_of_web_service_specifications
|
||||
- HTTP OPTIONS method and other HTTP methods: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS
|
||||
- HTTP POST requests: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST
|
||||
- Create web APIs with ASP.NET Core: https://docs.microsoft.com/en-us/aspnet/core/web-api/
|
||||
|
||||
## Web service routing
|
||||
- Design decisions around endpoint routing: https://devblogs.microsoft.com/aspnet/asp-net-core-2-2-0-preview1-endpoint-routing/
|
||||
- Endpoint routing: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing
|
||||
- Previous routing system: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-2.1
|
||||
- Route constraints: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing#route-constraint-reference
|
||||
- Dependency injection for ASP.NET Core: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection
|
||||
- Proposed standard for Problem Details for HTTP APIs: https://tools.ietf.org/html/rfc7807
|
||||
- Implementing problem details: https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.problemdetails
|
||||
|
||||
## Web service clients
|
||||
- REST Client: https://github.com/Huachao/vscode-restclient/blob/master/README.md
|
||||
- It is the BaseAddress and DefaultRequestHeaders properties that you should treat with caution with multiple threads: https://medium.com/@nuno.caneco/c-httpclient-should-not-be-disposed-or-should-it-45d2a8f568bc
|
||||
- You're using HttpClient wrong and it is destabilizing your software: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/
|
||||
- How to initiate HTTP requests: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests
|
||||
- Issues with the original HttpClient class available in .NET: https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests#issues-with-the-original-httpclient-class-available-in-net
|
||||
- HttpClient extension methods for easily working with JSON: https://github.com/dotnet/designs/blob/main/accepted/2020/json-http-extensions/json-http-extensions.md
|
||||
|
||||
## Documenting web services
|
||||
- Swagger: https://swagger.io/
|
||||
- Swagger Tools: https://swagger.io/tools/
|
||||
- Swashbuckle for ASP.NET Core: https://github.com/domaindrivendev/Swashbuckle.AspNetCore
|
||||
- How Swagger can support multiple versions of an API: https://stackoverflow.com/questions/30789045/leverage-multipleapiversions-in-swagger-with-attribute-versioning/30789944
|
||||
- Importance of documenting services: https://idratherbewriting.com/learnapidoc/
|
||||
- Benefits of setting version compatibility: https://docs.microsoft.com/en-us/aspnet/core/mvc/compatibility-version
|
||||
- Check latest version of analyzers package: http://www.nuget.org/packages/Microsoft.AspNetCore.Mvc.Api.Analyzers/
|
||||
|
||||
## Securing web services
|
||||
- Verifying that the tokens used to call your web APIs are requested with the expected claims: https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-protected-web-api-verification-scope-app-roles
|
||||
- CORS can be enabled to allow different origin requests: https://docs.microsoft.com/en-us/aspnet/core/security/cors
|
||||
- Common HTTP security headers that you might want to add: https://www.meziantou.net/security-headers-in-asp-net-core.htm
|
||||
|
||||
## Health checks and reliable web services
|
||||
- Health checks in ASP.NET Core: https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks
|
||||
- How to extend the health check response: https://blogs.msdn.microsoft.com/webdev/2018/08/22/asp-net-core-2-2-0-preview1-healthcheck/
|
||||
- How Polly can make your web services more reliable: https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/implement-http-call-retries-exponential-backoff-polly
|
||||
- Use HttpClientFactory to implement resilient HTTP requests: https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests
|
||||
- Redis: https://redis.io
|
||||
|
||||
# Chapter 17 - Building User Interfaces Using Blazor
|
||||
|
||||
## Blazor hosting models
|
||||
- Blazor: https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor
|
||||
- Official list of supported Blazor platforms: https://docs.microsoft.com/en-us/aspnet/core/blazor/supported-platforms
|
||||
- Blazor hosting models: https://docs.microsoft.com/en-us/aspnet/core/blazor/hosting-models
|
||||
- Blazor Mobile Bindings: https://devblogs.microsoft.com/aspnet/mobile-blazor-bindings-experiment/
|
||||
- Blazor Hybrid apps: https://devblogs.microsoft.com/aspnet/hybrid-blazor-apps-in-mobile-blazor-bindings-july-update/
|
||||
|
||||
## Blazor components
|
||||
- The reason for needing CSS isolation for Blazor components: https://github.com/dotnet/aspnetcore/issues/10170
|
||||
- OI icons: https://iconify.design/icon-sets/oi/
|
||||
- Setting `<head>` elements: https://docs.microsoft.com/en-us/aspnet/core/blazor/fundamentals/additional-scenarios-influence-html-head-tag-elements
|
||||
- Forms and validation: https://docs.microsoft.com/en-us/aspnet/core/blazor/forms-validation
|
||||
- NavigationManager with Blazor routes: https://docs.microsoft.com/en-us/aspnet/core/blazor/fundamentals/routing#uri-and-navigation-state-helpers
|
||||
|
||||
## Advanced techniques
|
||||
- Implementing offline support for Blazor WebAssembly projects: https://docs.microsoft.com/en-us/aspnet/core/blazor/progressive-web-app#offline-support
|
||||
- Lazy loading assemblies: https://docs.microsoft.com/en-us/aspnet/core/blazor/webassembly-lazy-load-assemblies?view=aspnetcore-5.0
|
||||
- Routing in Blazor Apps: Comparing the routing of popular web frameworks like React and Angular with Blazor: https://devblogs.microsoft.com/premier-developer/routing-in-blazor-apps/
|
||||
|
||||
## Other resources
|
||||
- Awesome Blazor: A collection of awesome Blazor resources: https://github.com/AdrienTorris/awesome-blazor
|
||||
- Blazor University: Learn the new .NET SPA framework from Microsoft: https://blazor-university.com
|
||||
- Blazor - app building workshop: In this workshop, we will build a complete Blazor app and learn about the various Blazor framework features along the way: https://github.com/dotnet-presentations/blazor-workshop/
|
||||
- Carl Franklin's Blazor Train: https://www.youtube.com/playlist?list=PL8h4jt35t1wjvwFnvcB2LlYL4jLRzRmoz
|
||||
- Welcome to PACMAN written in C# and running on Blazor WebAssembly: https://github.com/SteveDunn/PacManBlazor
|
||||
|
||||
# Chapter 18 - Building and Consuming Other Services
|
||||
|
||||
## WCF
|
||||
- Core WCF repository: https://github.com/CoreWCF/CoreWCF
|
||||
|
||||
## gRPC
|
||||
- gRPC: https://grpc.io
|
||||
- What’s new for gRPC in .NET 6: https://devblogs.microsoft.com/dotnet/grpc-in-dotnet-6/
|
||||
- Introduction to gRPC on .NET: https://docs.microsoft.com/en-us/aspnet/core/grpc/
|
||||
- gRPC as an alternative to WCF: https://devblogs.microsoft.com/premier-developer/grpc-asp-net-core-as-a-migration-path-for-wcfs-in-net-core/
|
||||
- How to use gRPC with ASP.NET Core: https://docs.microsoft.com/en-us/aspnet/core/grpc/aspnetcore
|
||||
|
||||
## SignalR
|
||||
- SignalR: https://docs.microsoft.com/en-us/aspnet/core/signalr/introduction
|
||||
|
||||
## OData
|
||||
- OData - the best way to REST: https://www.odata.org
|
||||
- OData documentation: https://docs.microsoft.com/en-us/odata/
|
||||
|
||||
## Azure Functions
|
||||
- Azure Functions documentation: https://docs.microsoft.com/en-us/azure/azure-functions/
|
||||
|
||||
# Chapter 19 - Building Mobile and Desktop Apps Using .NET MAUI
|
||||
|
||||
## .NET MAUI
|
||||
- Introduction to .NET MAUI: https://devblogs.microsoft.com/dotnet/introducing-net-multi-platform-app-ui/
|
||||
- GitHub repository for .NET MAUI: https://github.com/dotnet/maui
|
||||
|
||||
## Mobile development
|
||||
- If you want to use Visual Studio 2019 to create a mobile app, then you can read how to connect to a Mac build host: https://docs.microsoft.com/en-us/xamarin/ios/get-started/installation/windows/connecting-to-mac/
|
||||
- Pros and cons of the two major mobile platforms based on aspects such as revenue generation and user engagement: https://fueled.com/blog/app-store-vs-google-play/
|
||||
- ATS: https://docs.microsoft.com/en-us/xamarin/ios/app-fundamentals/ats
|
||||
- Handling self-signed certificates: https://docs.remotingsdk.com/Clients/Tasks/HandlingSelfSignedCertificates/NET/
|
||||
- Android and cleartext support: https://devblogs.microsoft.com/xamarin/cleartext-http-android-network-security/
|
||||
- Self Signed iOS Certifcates and Certificate Pinning in a Xamarin.Forms application: https://nicksnettravels.builttoroam.com/ios-certificate/
|
||||
- Protecting your users with certificate pinning: https://www.basdecort.com/protecting-your-users-with-certificate-pinning/
|
||||
- HttpClient and SSL/TLS implementation selector for iOS/macOS: https://docs.microsoft.com/en-us/xamarin/cross-platform/macios/http-stack
|
||||
|
||||
# Chapter 20 - Protecting Your Data and Applications
|
||||
|
||||
## Cross-platform cryptography
|
||||
- Features supported by which OS: https://docs.microsoft.com/en-us/dotnet/standard/security/cross-platform-cryptography
|
||||
|
||||
## General security knowledge
|
||||
- Key Security Concepts: https://docs.microsoft.com/en-us/dotnet/standard/security/key-security-concepts
|
||||
- Dictionary Attacks 101: https://blog.codinghorror.com/dictionary-attacks-101/
|
||||
- The first publicly known SHA1 collision happened in 2017: https://arstechnica.co.uk/information-technology/2017/02/at-deaths-door-for-years-widely-used-sha1-function-is-now-dead/
|
||||
|
||||
## Encryption
|
||||
- The RSA algorithm is based on the factorization of large integers: http://mathworld.wolfram.com/RSAEncryption.html
|
||||
- Encrypting Data: https://docs.microsoft.com/en-us/dotnet/standard/security/encrypting-data
|
||||
- Cryptographic Signatures: https://docs.microsoft.com/en-us/dotnet/standard/security/cryptographic-signatures
|
||||
|
||||
# Epilogue
|
||||
|
||||
## Next steps on your C# and .NET learning journey
|
||||
- Follow the Framework Design Guidelines: https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/
|
||||
- Follow the code style rules: https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/
|
||||
- Cloud-Native learning resources for .NET developers: https://devblogs.microsoft.com/dotnet/cloud-native-learning-resources-for-net-developers/
|
||||
- 37 App Ideas for Bootcamp Students & Code Newbies: https://dev.to/sylwiavargas/33-app-ideas-for-bootcamp-students-code-newbies-3n28
|
||||
- Just! Build! Websites! https://github.com/melanierichards/just-build-websites
|
||||
|
||||
## Learn from other Packt books
|
||||
- Mobile Development with .NET: https://www.packtpub.com/product/mobile-development-with-net-second-edition/9781800204690
|
||||
- Enterprise Application Development with C# 9 and .NET 5: https://www.packtpub.com/product/enterprise-application-development-with-c-9-and-net-5/9781800209442
|
||||
- Software Architecture with C# 9 and .NET 5: https://www.packtpub.com/product/software-architecture-with-c-9-and-net-5-second-edition/9781800566040
|
||||
- An Atypical ASP.NET Core 5 Design Patterns Guide: https://www.packtpub.com/product/an-atypical-asp-net-core-5-design-patterns-guide/9781789346091
|
||||
- Customizing ASP.NET Core 5.0: https://www.packtpub.com/product/customizing-asp-net-core-5-0/9781801077866
|
||||
- ASP.NET Core Secure Coding Cookbook: https://www.packtpub.com/product/asp-net-core-secure-coding-cookbook/9781801071567
|
||||
- ASP.NET Core 5 and React: https://www.packtpub.com/product/asp-net-core-5-and-react-second-edition/9781800206168
|
||||
- ASP.NET Core 5 and Angular: https://www.packtpub.com/product/asp-net-core-5-and-angular-fourth-edition/9781800560338
|
||||
- ASP.NET Core and Vue.js: https://www.packtpub.com/product/asp-net-core-and-vue-js/9781800206694
|
||||
- Practical Microservices with Dapr and .NET: https://www.packtpub.com/product/practical-microservices-with-dapr-and-net/9781800568372
|
||||
- Web Development with Blazor and .NET 5: https://www.packtpub.com/product/web-development-with-blazor-and-net-5/9781800208728
|
||||
- Mastering Windows Presentation Foundation: https://www.packtpub.com/product/mastering-windows-presentation-foundation-second-edition/9781838643416
|
||||
12
errata.md
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# Errata, Improvements and Troubleshooting
|
||||
|
||||
If you find any mistakes in the sixth edition, *C# 11 and .NET 7 - Modern Cross-Platform Development*, or if you have suggestions for improvements, then please [raise an issue in this repository](https://github.com/markjprice/cs11dotnet7/issues) or email me at markjprice (at) gmail.com.
|
||||
|
||||
- [Errata, Improvements and Troubleshooting](#errata-improvements-and-troubleshooting)
|
||||
- [Print Book](#print-book)
|
||||
- [Bonus Content](#bonus-content)
|
||||
|
||||
# Print Book
|
||||
|
||||
# Bonus Content
|
||||
|
||||
BIN
images/Categories-small/categories-small.jpeg
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
images/Categories-small/category1-small.jpeg
Normal file
|
After Width: | Height: | Size: 155 KiB |
BIN
images/Categories-small/category2-small.jpeg
Normal file
|
After Width: | Height: | Size: 147 KiB |
BIN
images/Categories-small/category3-small.jpeg
Normal file
|
After Width: | Height: | Size: 131 KiB |
BIN
images/Categories-small/category4-small.jpeg
Normal file
|
After Width: | Height: | Size: 171 KiB |
BIN
images/Categories-small/category5-small.jpeg
Normal file
|
After Width: | Height: | Size: 188 KiB |
BIN
images/Categories-small/category6-small.jpeg
Normal file
|
After Width: | Height: | Size: 190 KiB |
BIN
images/Categories-small/category7-small.jpeg
Normal file
|
After Width: | Height: | Size: 234 KiB |
BIN
images/Categories-small/category8-small.jpeg
Normal file
|
After Width: | Height: | Size: 177 KiB |
BIN
images/Categories/categories.jpeg
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
images/Categories/category1.jpeg
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
images/Categories/category2.jpeg
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
images/Categories/category3.jpeg
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
images/Categories/category4.jpeg
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
images/Categories/category5.jpeg
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
BIN
images/Categories/category6.jpeg
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
images/Categories/category7.jpeg
Normal file
|
After Width: | Height: | Size: 2 MiB |
BIN
images/Categories/category8.jpeg
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
images/csdotnet-7e.png
Normal file
|
After Width: | Height: | Size: 130 KiB |
20
notebooks/Chapter01.dib
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#!markdown
|
||||
|
||||
# Chapter 1 - Hello, C#! Welcome, .NET!
|
||||
Mixing *rich* **text** and code is cool!
|
||||
|
||||
#!csharp
|
||||
|
||||
#!about
|
||||
|
||||
#!csharp
|
||||
|
||||
Console.WriteLine("Hello, .NET Interactive!")
|
||||
|
||||
#!csharp
|
||||
|
||||
var number = 8;
|
||||
|
||||
#!csharp
|
||||
|
||||
Console.WriteLine(number);
|
||||
473
notebooks/Chapter02.dib
Normal file
|
|
@ -0,0 +1,473 @@
|
|||
#!csharp
|
||||
|
||||
#!markdown
|
||||
|
||||
# Chapter 2 - Speaking C#
|
||||
|
||||
#!csharp
|
||||
|
||||
using static System.Console;
|
||||
|
||||
#!markdown
|
||||
|
||||
## Showing the compiler version
|
||||
|
||||
#!csharp
|
||||
|
||||
// #error version
|
||||
|
||||
#!markdown
|
||||
|
||||
## Help for writing correct code
|
||||
|
||||
In a .NET Interactive notebook, you do not need to end the last statement with a semi-colon.
|
||||
|
||||
#!csharp
|
||||
|
||||
// with two compile errors
|
||||
Console.Writeline("Hello World!")
|
||||
|
||||
// fixed
|
||||
Console.WriteLine("Hello World!")
|
||||
|
||||
#!markdown
|
||||
|
||||
## Revealing the extent of the C# vocabulary
|
||||
|
||||
When running this code inside a .NET Interactive notebook, it lists all possible assemblies, including some used by the extension like `Microsoft.DotNet.Interactive`.
|
||||
|
||||
#!csharp
|
||||
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
// loop through the assemblies that this app references
|
||||
foreach (var r in Assembly.GetEntryAssembly()
|
||||
.GetReferencedAssemblies())
|
||||
{
|
||||
// load the assembly so we can read its details
|
||||
var a = Assembly.Load(new AssemblyName(r.FullName));
|
||||
|
||||
// declare a variable to count the number of methods
|
||||
int methodCount = 0;
|
||||
// loop through all the types in the assembly
|
||||
foreach (var t in a.DefinedTypes)
|
||||
{
|
||||
// add up the counts of methods
|
||||
methodCount += t.GetMethods().Count();
|
||||
}
|
||||
|
||||
// output the count of types and their methods
|
||||
Console.WriteLine(
|
||||
"{0:N0} types with {1:N0} methods in {2} assembly.",
|
||||
arg0: a.DefinedTypes.Count(),
|
||||
arg1: methodCount,
|
||||
arg2: r.Name);
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Naming things and assigning values
|
||||
|
||||
#!csharp
|
||||
|
||||
// let the heightInMetres variable become equal to the value 1.88
|
||||
double heightInMetres = 1.88;
|
||||
Console.WriteLine($"The variable {nameof(heightInMetres)} has the value {heightInMetres}.");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Storing text
|
||||
|
||||
#!csharp
|
||||
|
||||
char letter = 'A'; // assigning literal characters
|
||||
char digit = '1';
|
||||
char symbol = '$';
|
||||
char userChoice = GetSomeKeystroke(); // assigning from a fictitious function
|
||||
|
||||
string firstName = "Bob"; // assigning literal strings
|
||||
string lastName = "Smith";
|
||||
string phoneNumber = "(215) 555-4256";
|
||||
|
||||
// assigning a string returned from a fictitious function
|
||||
string address = GetAddressFromDatabase(id: 563);
|
||||
|
||||
#!markdown
|
||||
|
||||
## Understanding verbatim strings
|
||||
|
||||
In a .NET Interactive notebook, a brighter red color is used for escaped characters inside a string value to make them easier to see.
|
||||
|
||||
`\t` means tab. `\b` means backspace. `\s` does is not a valid escape character.
|
||||
|
||||
#!csharp
|
||||
|
||||
string fullNameWithTabSeparator = "Bob\tSmith";
|
||||
|
||||
string filePath = "C:\televisions\sony\bravia.txt";
|
||||
|
||||
#!csharp
|
||||
|
||||
string filePath = @"C:\televisions\sony\bravia.txt";
|
||||
|
||||
#!markdown
|
||||
|
||||
## Storing numbers
|
||||
|
||||
#!csharp
|
||||
|
||||
// unsigned integer means positive whole number or 0
|
||||
uint naturalNumber = 23;
|
||||
|
||||
// integer means negative or positive whole number or 0
|
||||
int integerNumber = -23;
|
||||
|
||||
// float means single-precision floating point
|
||||
// F suffix makes it a float literal
|
||||
float realNumber = 2.3F;
|
||||
|
||||
// double means double-precision floating point
|
||||
double anotherRealNumber = 2.3; // double literal
|
||||
|
||||
#!markdown
|
||||
|
||||
## Storing whole numbers
|
||||
|
||||
#!csharp
|
||||
|
||||
// three variables that store the number 2 million
|
||||
int decimalNotation = 2_000_000;
|
||||
int binaryNotation = 0b_0001_1110_1000_0100_1000_0000;
|
||||
int hexadecimalNotation = 0x_001E_8480;
|
||||
|
||||
// check the three variables have the same value
|
||||
// both statements output true
|
||||
Console.WriteLine($"{decimalNotation == binaryNotation}");
|
||||
Console.WriteLine(
|
||||
$"{decimalNotation == hexadecimalNotation}");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Writing code to explore number sizes
|
||||
|
||||
#!csharp
|
||||
|
||||
Console.WriteLine($"int uses {sizeof(int)} bytes and can store numbers in the range {int.MinValue:N0} to {int.MaxValue:N0}.");
|
||||
Console.WriteLine($"double uses {sizeof(double)} bytes and can store numbers in the range {double.MinValue:N0} to {double.MaxValue:N0}.");
|
||||
Console.WriteLine($"decimal uses {sizeof(decimal)} bytes and can store numbers in the range {decimal.MinValue:N0} to {decimal.MaxValue:N0}.");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Comparing double and decimal
|
||||
|
||||
#!csharp
|
||||
|
||||
Console.WriteLine("Using doubles:");
|
||||
double a = 0.1;
|
||||
double b = 0.2;
|
||||
|
||||
if (a + b == 0.3)
|
||||
{
|
||||
Console.WriteLine($"{a} + {b} equals 0.3");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"{a} + {b} does NOT equal 0.3");
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
Console.WriteLine("Using decimals:");
|
||||
decimal c = 0.1M; // M suffix means a decimal literal value
|
||||
decimal d = 0.2M;
|
||||
|
||||
if (c + d == 0.3M)
|
||||
{
|
||||
Console.WriteLine($"{c} + {d} equals 0.3");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"{c} + {d} does NOT equal 0.3");
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Storing Booleans
|
||||
|
||||
#!csharp
|
||||
|
||||
bool happy = true;
|
||||
bool sad = false;
|
||||
|
||||
#!markdown
|
||||
|
||||
## Storing any type of object
|
||||
|
||||
#!csharp
|
||||
|
||||
object height = 1.88; // storing a double in an object
|
||||
object name = "Amir"; // storing a string in an object
|
||||
Console.WriteLine($"{name} is {height} metres tall.");
|
||||
|
||||
//int length1 = name.Length; // gives compile error!
|
||||
int length2 = ((string)name).Length; // tell compiler it is a string
|
||||
Console.WriteLine($"{name} has {length2} characters.");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Storing dynamic types
|
||||
|
||||
#!csharp
|
||||
|
||||
// storing a string in a dynamic object
|
||||
// string has a Length property
|
||||
dynamic anotherName = "Ahmed";
|
||||
|
||||
// int does not have a Length property
|
||||
anotherName = 12;
|
||||
|
||||
// an array of any type has a Length property
|
||||
anotherName = new[] { 3, 5, 7 };
|
||||
|
||||
// this compiles but would throw an exception at run-time
|
||||
// if you later store a data type that does not have a
|
||||
// property named Length
|
||||
Console.WriteLine($"Length is {anotherName.Length}");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Specifying and inferring the type of a local variable
|
||||
|
||||
*Note*: click **Execute Code** in the following cell to import the namespaces for the subsequent code cell.
|
||||
|
||||
#!csharp
|
||||
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
|
||||
#!csharp
|
||||
|
||||
var population = 66_000_000; // 66 million in UK
|
||||
var weight = 1.88; // in kilograms
|
||||
var price = 4.99M; // in pounds sterling
|
||||
var fruit = "Apples"; // strings use double-quotes
|
||||
var letter = 'Z'; // chars use single-quotes
|
||||
var happy = true; // Booleans have value of true or false
|
||||
|
||||
// good use of var because it avoids the repeated type
|
||||
// as shown in the more verbose second statement
|
||||
var xml1 = new XmlDocument();
|
||||
XmlDocument xml2 = new XmlDocument();
|
||||
|
||||
// bad use of var because we cannot tell the type, so we
|
||||
// should use a specific type declaration as shown in
|
||||
// the second statement
|
||||
var file1 = File.CreateText(@"C:\something.txt");
|
||||
StreamWriter file2 = File.CreateText(@"C:\something.txt");
|
||||
|
||||
XmlDocument xml3 = new(); // target-typed new in C# 9 or later
|
||||
|
||||
#!csharp
|
||||
|
||||
class Person
|
||||
{
|
||||
public DateTime BirthDate;
|
||||
}
|
||||
|
||||
Person kim = new();
|
||||
kim.BirthDate = new(1967, 12, 26); // instead of: new DateTime(1967, 12, 26)
|
||||
|
||||
#!markdown
|
||||
|
||||
## Getting default values for types
|
||||
|
||||
The default value of a `string` is `null` which outputs as nothing.
|
||||
|
||||
#!csharp
|
||||
|
||||
Console.WriteLine($"default(int) = {default(int)}");
|
||||
Console.WriteLine($"default(bool) = {default(bool)}");
|
||||
Console.WriteLine(
|
||||
$"default(DateTime) = {default(DateTime)}");
|
||||
Console.WriteLine(
|
||||
$"default(string) = {default(string)}");
|
||||
|
||||
#!csharp
|
||||
|
||||
int number = 13;
|
||||
Console.WriteLine($"number has been set to: {number}");
|
||||
number = default;
|
||||
Console.WriteLine($"number has been reset to its default: {number}");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Storing multiple values an array
|
||||
|
||||
#!csharp
|
||||
|
||||
string[] names; // can reference any array of strings
|
||||
|
||||
// allocating memory for four strings in an array
|
||||
names = new string[4];
|
||||
|
||||
// storing items at index positions
|
||||
names[0] = "Kate";
|
||||
names[1] = "Jack";
|
||||
names[2] = "Rebecca";
|
||||
names[3] = "Tom";
|
||||
|
||||
// looping through the names
|
||||
for (int i = 0; i < names.Length; i++)
|
||||
{
|
||||
// output the item at index position i
|
||||
Console.WriteLine(names[i]);
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Making a value type nullable
|
||||
|
||||
#!csharp
|
||||
|
||||
int thisCannotBeNull = 4;
|
||||
// thisCannotBeNull = null; // compile error!
|
||||
|
||||
int? thisCouldBeNull = null;
|
||||
Console.WriteLine(thisCouldBeNull);
|
||||
Console.WriteLine(thisCouldBeNull.GetValueOrDefault());
|
||||
|
||||
thisCouldBeNull = 7;
|
||||
Console.WriteLine(thisCouldBeNull);
|
||||
Console.WriteLine(thisCouldBeNull.GetValueOrDefault());
|
||||
|
||||
#!markdown
|
||||
|
||||
## Checking for null
|
||||
|
||||
#!csharp
|
||||
|
||||
string authorName = null;
|
||||
|
||||
// the following throws a NullReferenceException
|
||||
// int x = authorName.Length;
|
||||
|
||||
// instead of throwing an exception, null is assigned to y
|
||||
int? y = authorName?.Length;
|
||||
|
||||
Console.WriteLine($"y is null: {y is null}");
|
||||
|
||||
// result will be 3 if authorName?.Length is null
|
||||
var result = authorName?.Length ?? 3;
|
||||
Console.WriteLine(result);
|
||||
|
||||
#!markdown
|
||||
|
||||
## Formatting using numbered positional arguments
|
||||
|
||||
#!csharp
|
||||
|
||||
using static System.Console;
|
||||
|
||||
#!csharp
|
||||
|
||||
int numberOfApples = 12;
|
||||
decimal pricePerApple = 0.35M;
|
||||
|
||||
WriteLine(
|
||||
format: "{0} apples costs {1:C}",
|
||||
arg0: numberOfApples,
|
||||
arg1: pricePerApple * numberOfApples);
|
||||
|
||||
string formatted = string.Format(
|
||||
format: "{0} apples costs {1:C}",
|
||||
arg0: numberOfApples,
|
||||
arg1: pricePerApple * numberOfApples);
|
||||
|
||||
//WriteToFile(formatted); // writes the string into a file
|
||||
|
||||
#!markdown
|
||||
|
||||
## Formatting using interpolated strings
|
||||
|
||||
#!csharp
|
||||
|
||||
WriteLine($"{numberOfApples} apples costs {pricePerApple * numberOfApples:C}");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Understanding format strings
|
||||
|
||||
#!csharp
|
||||
|
||||
string applesText = "Apples";
|
||||
int applesCount = 1234;
|
||||
|
||||
string bananasText = "Bananas";
|
||||
int bananasCount = 56789;
|
||||
|
||||
WriteLine(
|
||||
format: "{0,-8} {1,6:N0}",
|
||||
arg0: "Name",
|
||||
arg1: "Count");
|
||||
|
||||
WriteLine(
|
||||
format: "{0,-8} {1,6:N0}",
|
||||
arg0: applesText,
|
||||
arg1: applesCount);
|
||||
|
||||
WriteLine(
|
||||
format: "{0,-8} {1,6:N0}",
|
||||
arg0: bananasText,
|
||||
arg1: bananasCount);
|
||||
|
||||
#!markdown
|
||||
|
||||
## Getting text input from the user
|
||||
|
||||
.NET Interactive notebooks do not support `ReadLine()` so in the following code we must set literal string values for the two variables.
|
||||
|
||||
#!csharp
|
||||
|
||||
Write("Type your first name and press ENTER: ");
|
||||
string firstName = "Gary"; // cannot use Console.ReadLine()
|
||||
|
||||
Write("Type your age and press ENTER: ");
|
||||
string age = "34"; // cannot use Console.ReadLine()
|
||||
|
||||
WriteLine(
|
||||
$"Hello {firstName}, you look good for {age}.");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Getting key input from the user
|
||||
|
||||
`Console.ReadKey()` does not work in a .NET notebook.
|
||||
|
||||
#!markdown
|
||||
|
||||
## Getting arguments
|
||||
|
||||
Arguments cannot be passed to a .NET notebook.
|
||||
|
||||
#!markdown
|
||||
|
||||
## Exercise 3 - Practice number sizes and ranges
|
||||
|
||||
#!csharp
|
||||
|
||||
WriteLine("--------------------------------------------------------------------------");
|
||||
WriteLine("Type Byte(s) of memory Min Max");
|
||||
WriteLine("--------------------------------------------------------------------------");
|
||||
WriteLine($"sbyte {sizeof(sbyte),-4} {sbyte.MinValue,30} {sbyte.MaxValue,30}");
|
||||
WriteLine($"byte {sizeof(byte),-4} {byte.MinValue,30} {byte.MaxValue,30}");
|
||||
WriteLine($"short {sizeof(short),-4} {short.MinValue,30} {short.MaxValue,30}");
|
||||
WriteLine($"ushort {sizeof(ushort),-4} {ushort.MinValue,30} {ushort.MaxValue,30}");
|
||||
WriteLine($"int {sizeof(int),-4} {int.MinValue,30} {int.MaxValue,30}");
|
||||
WriteLine($"uint {sizeof(uint),-4} {uint.MinValue,30} {uint.MaxValue,30}");
|
||||
WriteLine($"long {sizeof(long),-4} {long.MinValue,30} {long.MaxValue,30}");
|
||||
WriteLine($"ulong {sizeof(ulong),-4} {ulong.MinValue,30} {ulong.MaxValue,30}");
|
||||
WriteLine($"float {sizeof(float),-4} {float.MinValue,30} {float.MaxValue,30}");
|
||||
WriteLine($"double {sizeof(double),-4} {double.MinValue,30} {double.MaxValue,30}");
|
||||
WriteLine($"decimal {sizeof(decimal),-4} {decimal.MinValue,30} {decimal.MaxValue,30}");
|
||||
WriteLine("--------------------------------------------------------------------------");
|
||||
582
notebooks/Chapter03.dib
Normal file
|
|
@ -0,0 +1,582 @@
|
|||
#!markdown
|
||||
|
||||
# Chapter 3 - Controlling Flow and Converting Types
|
||||
|
||||
#!markdown
|
||||
|
||||
The following code cell statically imports the Console class so that we can call the `WriteLine` method anywhere.
|
||||
|
||||
#!csharp
|
||||
|
||||
using static System.Console;
|
||||
|
||||
#!markdown
|
||||
|
||||
# Operating on variables
|
||||
|
||||
#!markdown
|
||||
|
||||
## Exploring unary operators
|
||||
|
||||
#!csharp
|
||||
|
||||
int a = 3;
|
||||
int b = a++;
|
||||
WriteLine($"a is {a}, b is {b}");
|
||||
|
||||
int c = 3;
|
||||
int d = ++c; // increment c before assigning it
|
||||
WriteLine($"c is {c}, d is {d}");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Exploring binary arithmetic operators
|
||||
|
||||
#!csharp
|
||||
|
||||
int e = 11;
|
||||
int f = 3;
|
||||
WriteLine($"e is {e}, f is {f}");
|
||||
WriteLine($"e + f = {e + f}");
|
||||
WriteLine($"e - f = {e - f}");
|
||||
WriteLine($"e * f = {e * f}");
|
||||
WriteLine($"e / f = {e / f}");
|
||||
WriteLine($"e % f = {e % f}");
|
||||
|
||||
double g = 11.0;
|
||||
WriteLine($"g is {g:N1}, f is {f}");
|
||||
WriteLine($"g / f = {g / f}");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Exploring logical operators
|
||||
|
||||
#!csharp
|
||||
|
||||
bool a = true;
|
||||
bool b = false;
|
||||
WriteLine($"AND | a | b ");
|
||||
WriteLine($"a | {a & a,-5} | {a & b,-5} ");
|
||||
WriteLine($"b | {b & a,-5} | {b & b,-5} ");
|
||||
WriteLine();
|
||||
WriteLine($"OR | a | b ");
|
||||
WriteLine($"a | {a | a,-5} | {a | b,-5} ");
|
||||
WriteLine($"b | {b | a,-5} | {b | b,-5} ");
|
||||
WriteLine();
|
||||
WriteLine($"XOR | a | b ");
|
||||
WriteLine($"a | {a ^ a,-5} | {a ^ b,-5} ");
|
||||
WriteLine($"b | {b ^ a,-5} | {b ^ b,-5} ");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Exploring conditional logical operators
|
||||
|
||||
#!csharp
|
||||
|
||||
private static bool DoStuff()
|
||||
{
|
||||
WriteLine("I am doing some stuff.");
|
||||
return true;
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
WriteLine($"a & DoStuff() = {a & DoStuff()}");
|
||||
WriteLine($"b & DoStuff() = {b & DoStuff()}");
|
||||
|
||||
#!csharp
|
||||
|
||||
WriteLine($"a && DoStuff() = {a && DoStuff()}");
|
||||
WriteLine($"b && DoStuff() = {b && DoStuff()}");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Exploring bitwise and binary shift operators
|
||||
|
||||
#!csharp
|
||||
|
||||
int a = 10; // 0000 1010
|
||||
int b = 6; // 0000 0110
|
||||
WriteLine($"a = {a}");
|
||||
WriteLine($"b = {b}");
|
||||
WriteLine($"a & b = {a & b}"); // 2-bit column only
|
||||
WriteLine($"a | b = {a | b}"); // 8, 4, and 2-bit columns
|
||||
WriteLine($"a ^ b = {a ^ b}"); // 8 and 4-bit columns
|
||||
|
||||
#!markdown
|
||||
|
||||
## Miscellaneous operators
|
||||
|
||||
#!csharp
|
||||
|
||||
int age = 47;
|
||||
|
||||
// How many operators in the following statement?
|
||||
char firstDigit = age.ToString()[0];
|
||||
|
||||
// There are four operators:
|
||||
// = is the assignment operator
|
||||
// . is the member access operator
|
||||
// () is the invocation operator
|
||||
// [] is the indexer access operator
|
||||
|
||||
#!markdown
|
||||
|
||||
# Understanding selection statements
|
||||
|
||||
#!markdown
|
||||
|
||||
## Branching with the if statement
|
||||
|
||||
#!csharp
|
||||
|
||||
string password = "ninja";
|
||||
|
||||
if (password.Length < 8)
|
||||
{
|
||||
WriteLine("Your password is too short. Use at least 8 characters.");
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLine("Your password is strong.");
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Pattern matching with the if statement
|
||||
|
||||
#!csharp
|
||||
|
||||
// add and remove the "" to change the behavior
|
||||
object o = 3;
|
||||
int j = 4;
|
||||
|
||||
if (o is int i)
|
||||
{
|
||||
WriteLine($"{i} x {j} = {i * j}");
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLine("o is not an int so it cannot multiply!");
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Branching with the switch statement
|
||||
|
||||
#!csharp
|
||||
|
||||
int number = (new Random()).Next(1, 7);
|
||||
WriteLine($"My random number is {number}");
|
||||
|
||||
switch (number)
|
||||
{
|
||||
case 1:
|
||||
WriteLine("One");
|
||||
break; // jumps to end of switch statement
|
||||
case 2:
|
||||
WriteLine("Two");
|
||||
goto case 1;
|
||||
case 3: // multiple case section
|
||||
case 4:
|
||||
WriteLine("Three or four");
|
||||
goto case 1;
|
||||
case 5:
|
||||
// go to sleep for half a second
|
||||
System.Threading.Thread.Sleep(500);
|
||||
goto A_label;
|
||||
default:
|
||||
WriteLine("Default");
|
||||
break;
|
||||
} // end of switch statement
|
||||
|
||||
WriteLine("After end of switch");
|
||||
A_label:
|
||||
WriteLine($"After A_label");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Pattern matching with the switch statement
|
||||
|
||||
#!csharp
|
||||
|
||||
using System.IO;
|
||||
|
||||
#!markdown
|
||||
|
||||
`ReadKey` is not supported in .NET Interactive Notebooks. To simulate the user pressing R or W, the following code instantiates a ConsoleKeyInfo object that represents the R or W key being pressed.
|
||||
|
||||
#!markdown
|
||||
|
||||
Comment and uncomment the appropriate path depending on your OS.
|
||||
|
||||
#!csharp
|
||||
|
||||
//string path = "/Users/markjprice/Code/Chapter03";
|
||||
string path = @"C:\Code\Chapter03";
|
||||
|
||||
Write("Press R for read-only or W for writeable: ");
|
||||
|
||||
ConsoleKeyInfo key = // ReadKey();
|
||||
new ConsoleKeyInfo('R', ConsoleKey.R,
|
||||
shift: false, alt: false, control: false);
|
||||
|
||||
WriteLine();
|
||||
|
||||
Stream s = null;
|
||||
|
||||
if (key.Key == ConsoleKey.R)
|
||||
{
|
||||
s = File.Open(
|
||||
Path.Combine(path, "file.txt"),
|
||||
FileMode.OpenOrCreate,
|
||||
FileAccess.Read);
|
||||
}
|
||||
else
|
||||
{
|
||||
s = File.Open(
|
||||
Path.Combine(path, "file.txt"),
|
||||
FileMode.OpenOrCreate,
|
||||
FileAccess.Write);
|
||||
}
|
||||
|
||||
string message = string.Empty;
|
||||
|
||||
switch (s)
|
||||
{
|
||||
case FileStream writeableFile when s.CanWrite:
|
||||
message = "The stream is a file that I can write to.";
|
||||
break;
|
||||
case FileStream readOnlyFile:
|
||||
message = "The stream is a read-only file.";
|
||||
break;
|
||||
case MemoryStream ms:
|
||||
message = "The stream is a memory address.";
|
||||
break;
|
||||
default: // always evaluated last despite its current position
|
||||
message = "The stream is some other type.";
|
||||
break;
|
||||
case null:
|
||||
message = "The stream is null.";
|
||||
break;
|
||||
}
|
||||
WriteLine(message);
|
||||
|
||||
#!markdown
|
||||
|
||||
## Simplifying switch statements with switch expressions
|
||||
|
||||
#!csharp
|
||||
|
||||
message = s switch
|
||||
{
|
||||
FileStream writeableFile when s.CanWrite
|
||||
=> "The stream is a file that I can write to.",
|
||||
FileStream readOnlyFile
|
||||
=> "The stream is a read-only file.",
|
||||
MemoryStream ms
|
||||
=> "The stream is a memory address.",
|
||||
null
|
||||
=> "The stream is null.",
|
||||
_
|
||||
=> "The stream is some other type."
|
||||
};
|
||||
WriteLine(message);
|
||||
|
||||
#!markdown
|
||||
|
||||
# Understanding iteration statements
|
||||
|
||||
#!markdown
|
||||
|
||||
## Looping with the while statement
|
||||
|
||||
#!csharp
|
||||
|
||||
int x = 0;
|
||||
|
||||
while (x < 10)
|
||||
{
|
||||
WriteLine(x);
|
||||
x++;
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Looping with the do statement
|
||||
|
||||
#!csharp
|
||||
|
||||
string password = string.Empty;
|
||||
int attempts = 0;
|
||||
|
||||
do
|
||||
{
|
||||
if (attempts == 10)
|
||||
{
|
||||
break;
|
||||
}
|
||||
Write("Enter your password: ");
|
||||
password = "Pa$$w0rd"; // ReadLine();
|
||||
attempts++;
|
||||
}
|
||||
while (password != "Pa$$w0rd");
|
||||
|
||||
if (attempts == 10)
|
||||
{
|
||||
WriteLine("You failed after 10 attempts!");
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLine("Correct!");
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Looping with the for statement
|
||||
|
||||
#!csharp
|
||||
|
||||
for (int y = 1; y <= 10; y++)
|
||||
{
|
||||
WriteLine(y);
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Looping with the foreach statement
|
||||
|
||||
#!csharp
|
||||
|
||||
string[] names = { "Adam", "Barry", "Charlie" };
|
||||
|
||||
foreach (string name in names)
|
||||
{
|
||||
WriteLine($"{name} has {name.Length} characters.");
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
# Casting and converting between types
|
||||
|
||||
#!csharp
|
||||
|
||||
using static System.Console;
|
||||
using static System.Convert;
|
||||
|
||||
#!markdown
|
||||
|
||||
## Casting numbers implicitly and explicitly
|
||||
|
||||
#!csharp
|
||||
|
||||
int a = 10;
|
||||
double b = a; // an int can be safely cast into a double
|
||||
WriteLine(b);
|
||||
|
||||
double c = 9.8;
|
||||
int d = (int)c; // compiler gives an error for this line before adding (int)
|
||||
WriteLine(d); // d is 9 losing the .8 part due to the (int) cast above
|
||||
|
||||
long e = 10;
|
||||
int f = (int)e;
|
||||
WriteLine($"e is {e:N0} and f is {f:N0}");
|
||||
e = 5_000_000_000; // long.MaxValue;
|
||||
f = (int)e;
|
||||
WriteLine($"e is {e:N0} and f is {f:N0}");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Converting with the System.Convert type
|
||||
|
||||
#!csharp
|
||||
|
||||
double g = 9.8;
|
||||
int h = ToInt32(g); // a method of System.Convert
|
||||
WriteLine($"g is {g} and h is {h}");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Understanding the default rounding rules
|
||||
|
||||
#!csharp
|
||||
|
||||
double[] doubles = new[]
|
||||
{ 9.49, 9.5, 9.51, 10.49, 10.5, 10.51 };
|
||||
|
||||
foreach (double n in doubles)
|
||||
{
|
||||
WriteLine($"ToInt32({n}) is {ToInt32(n)}");
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Taking control of rounding rules
|
||||
|
||||
#!csharp
|
||||
|
||||
foreach (double n in doubles)
|
||||
{
|
||||
WriteLine(format:
|
||||
"Math.Round({0}, 0, MidpointRounding.AwayFromZero) is {1}",
|
||||
arg0: n,
|
||||
arg1: Math.Round(value: n, digits: 0,
|
||||
mode: MidpointRounding.AwayFromZero));
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Converting from any type to a string
|
||||
|
||||
#!csharp
|
||||
|
||||
int number = 12;
|
||||
WriteLine(number.ToString());
|
||||
|
||||
bool boolean = true;
|
||||
WriteLine(boolean.ToString());
|
||||
|
||||
DateTime now = DateTime.Now;
|
||||
WriteLine(now.ToString());
|
||||
|
||||
object me = new object();
|
||||
WriteLine(me.ToString());
|
||||
|
||||
#!markdown
|
||||
|
||||
## Converting from a binary object to a string
|
||||
|
||||
#!csharp
|
||||
|
||||
// allocate array of 128 bytes
|
||||
byte[] binaryObject = new byte[128];
|
||||
|
||||
// populate array with random bytes
|
||||
(new Random()).NextBytes(binaryObject);
|
||||
|
||||
WriteLine("Binary Object as bytes:");
|
||||
|
||||
for(int index = 0; index < binaryObject.Length; index++)
|
||||
{
|
||||
Write($"{binaryObject[index]:X} ");
|
||||
}
|
||||
WriteLine();
|
||||
|
||||
// convert to Base64 string and output as text
|
||||
string encoded = ToBase64String(binaryObject);
|
||||
|
||||
WriteLine($"Binary Object as Base64: {encoded}");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Parsing from strings to numbers or dates and times
|
||||
|
||||
#!csharp
|
||||
|
||||
int age = int.Parse("27");
|
||||
DateTime birthday = DateTime.Parse("4 July 1980");
|
||||
|
||||
WriteLine($"I was born {age} years ago.");
|
||||
WriteLine($"My birthday is {birthday}.");
|
||||
WriteLine($"My birthday is {birthday:D}.");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Errors using Parse
|
||||
|
||||
#!csharp
|
||||
|
||||
int count = int.Parse("abc");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Avoiding exceptions using the TryParse method
|
||||
|
||||
#!csharp
|
||||
|
||||
Write("How many eggs are there? ");
|
||||
string input = "12"; // change to "twelve"
|
||||
|
||||
if (int.TryParse(input, out int count))
|
||||
{
|
||||
WriteLine($"There are {count} eggs.");
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLine("I could not parse the input.");
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Wrapping error-prone code in a try block
|
||||
|
||||
#!csharp
|
||||
|
||||
WriteLine("Before parsing");
|
||||
Write("What is your age? ");
|
||||
string input = "49";
|
||||
|
||||
try
|
||||
{
|
||||
int age = int.Parse(input);
|
||||
WriteLine($"You are {age} years old.");
|
||||
}
|
||||
catch (OverflowException)
|
||||
{
|
||||
WriteLine("Your age is a valid number format but it is either too big or small.");
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
WriteLine("The age you entered is not a valid number format.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteLine($"{ex.GetType()} says {ex.Message}");
|
||||
}
|
||||
|
||||
WriteLine("After parsing");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Throwing overflow exceptions with the checked statement
|
||||
|
||||
#!csharp
|
||||
|
||||
try
|
||||
{
|
||||
checked
|
||||
{
|
||||
int x = int.MaxValue - 1;
|
||||
WriteLine($"Initial value: {x}");
|
||||
x++;
|
||||
WriteLine($"After incrementing: {x}");
|
||||
x++;
|
||||
WriteLine($"After incrementing: {x}");
|
||||
x++;
|
||||
WriteLine($"After incrementing: {x}");
|
||||
}
|
||||
}
|
||||
catch (OverflowException)
|
||||
{
|
||||
WriteLine("The code overflowed but I caught the exception.");
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Disabling compiler overflow checks with the unchecked statement
|
||||
|
||||
#!csharp
|
||||
|
||||
int y = int.MaxValue + 1;
|
||||
|
||||
#!csharp
|
||||
|
||||
unchecked
|
||||
{
|
||||
int y = int.MaxValue + 1;
|
||||
WriteLine($"Initial value: {y}");
|
||||
y--;
|
||||
WriteLine($"After decrementing: {y}");
|
||||
y--;
|
||||
WriteLine($"After decrementing: {y}");
|
||||
}
|
||||
245
notebooks/Chapter04.dib
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
#!markdown
|
||||
|
||||
# Chapter 4 - Writing, Debugging, and Testing Functions
|
||||
|
||||
#!csharp
|
||||
|
||||
// allow simplified WriteLine and other console method calls
|
||||
using static System.Console;
|
||||
|
||||
#!markdown
|
||||
|
||||
## Writing a times table function
|
||||
|
||||
#!csharp
|
||||
|
||||
static void TimesTable(byte number)
|
||||
{
|
||||
WriteLine($"This is the {number} times table:");
|
||||
|
||||
for (int row = 1; row <= 12; row++)
|
||||
{
|
||||
WriteLine($"{row} x {number} = {row * number}");
|
||||
}
|
||||
WriteLine();
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
TimesTable(6) // semicolon is optional in a .NET notebook
|
||||
|
||||
#!csharp
|
||||
|
||||
TimesTable(number: 87) // optional to specify the parameter name
|
||||
|
||||
#!markdown
|
||||
|
||||
## Writing a function that returns a value
|
||||
|
||||
#!csharp
|
||||
|
||||
static decimal CalculateTax(
|
||||
decimal amount, string twoLetterRegionCode)
|
||||
{
|
||||
decimal rate = 0.0M;
|
||||
|
||||
switch (twoLetterRegionCode)
|
||||
{
|
||||
case "CH": // Switzerland
|
||||
rate = 0.08M;
|
||||
break;
|
||||
case "DK": // Denmark
|
||||
case "NO": // Norway
|
||||
rate = 0.25M;
|
||||
break;
|
||||
case "GB": // United Kingdom
|
||||
case "FR": // France
|
||||
rate = 0.2M;
|
||||
break;
|
||||
case "HU": // Hungary
|
||||
rate = 0.27M;
|
||||
break;
|
||||
case "OR": // Oregon
|
||||
case "AK": // Alaska
|
||||
case "MT": // Montana
|
||||
rate = 0.0M;
|
||||
break;
|
||||
case "ND": // North Dakota
|
||||
case "WI": // Wisconsin
|
||||
case "ME": // Maine
|
||||
case "VA": // Virginia
|
||||
rate = 0.05M;
|
||||
break;
|
||||
case "CA": // California
|
||||
rate = 0.0825M;
|
||||
break;
|
||||
default: // most US states
|
||||
rate = 0.06M;
|
||||
break;
|
||||
}
|
||||
|
||||
return amount * rate;
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
decimal taxToPay = CalculateTax(149, "FR");
|
||||
WriteLine($"You must pay {taxToPay} in tax.");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Converting numbers from cardinal to ordinal
|
||||
|
||||
#!csharp
|
||||
|
||||
/// <summary>
|
||||
/// Pass a 32-bit integer and it will be converted into its ordinal equivalent.
|
||||
/// </summary>
|
||||
/// <param name="number">Number is a cardinal value e.g. 1, 2, 3, and so on.</param>
|
||||
/// <returns>Number as an ordinal value e.g. 1st, 2nd, 3rd, and so on.</ returns>
|
||||
static string CardinalToOrdinal(int number)
|
||||
{
|
||||
switch (number)
|
||||
{
|
||||
case 11: // special cases for 11th to 13th
|
||||
case 12:
|
||||
case 13:
|
||||
return $"{number}th";
|
||||
default:
|
||||
int lastDigit = number % 10;
|
||||
|
||||
string suffix = lastDigit switch
|
||||
{
|
||||
1 => "st",
|
||||
2 => "nd",
|
||||
3 => "rd",
|
||||
_ => "th"
|
||||
};
|
||||
return $"{number}{suffix}";
|
||||
}
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
static void RunCardinalToOrdinal()
|
||||
{
|
||||
for (int number = 1; number <= 40; number++)
|
||||
{
|
||||
Write($"{CardinalToOrdinal(number)} ");
|
||||
}
|
||||
WriteLine();
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
RunCardinalToOrdinal()
|
||||
|
||||
#!markdown
|
||||
|
||||
## Calculating factorials with recursion
|
||||
|
||||
#!csharp
|
||||
|
||||
static int Factorial(int number)
|
||||
{
|
||||
if (number < 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if (number == 1)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
checked // for overflow
|
||||
{
|
||||
return number * Factorial(number - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
static void RunFactorial()
|
||||
{
|
||||
for (int i = 1; i < 15; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
WriteLine($"{i}! = {Factorial(i):N0}");
|
||||
}
|
||||
catch (System.OverflowException)
|
||||
{
|
||||
WriteLine($"{i}! is too big for a 32-bit integer.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
RunFactorial()
|
||||
|
||||
#!markdown
|
||||
|
||||
## Using lambdas in function implementations
|
||||
|
||||
#!csharp
|
||||
|
||||
static int FibImperative(int term)
|
||||
{
|
||||
if (term == 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if (term == 2)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FibImperative(term - 1) + FibImperative(term - 2);
|
||||
}
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
static void RunFibImperative()
|
||||
{
|
||||
for (int i = 1; i <= 30; i++)
|
||||
{
|
||||
WriteLine("The {0} term of the Fibonacci sequence is {1:N0}.",
|
||||
arg0: CardinalToOrdinal(i),
|
||||
arg1: FibImperative(term: i));
|
||||
}
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
RunFibImperative()
|
||||
|
||||
#!csharp
|
||||
|
||||
static int FibFunctional(int term) =>
|
||||
term switch
|
||||
{
|
||||
1 => 0,
|
||||
2 => 1,
|
||||
_ => FibFunctional(term - 1) + FibFunctional(term - 2)
|
||||
};
|
||||
|
||||
#!csharp
|
||||
|
||||
static void RunFibFunctional()
|
||||
{
|
||||
for (int i = 1; i <= 30; i++)
|
||||
{
|
||||
WriteLine("The {0} term of the Fibonacci sequence is {1:N0}.",
|
||||
arg0: CardinalToOrdinal(i),
|
||||
arg1: FibFunctional(term: i));
|
||||
}
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
RunFibFunctional()
|
||||
655
notebooks/Chapter05.dib
Normal file
|
|
@ -0,0 +1,655 @@
|
|||
#!markdown
|
||||
|
||||
# Chapter 5 - Building Your Own Types with Object-Oriented Programming
|
||||
|
||||
Execute the following code cell to make `Console` methods available in every code cell in this notebook.
|
||||
|
||||
We cannot create a class library in a .NET Interactive notebook so we will put all code in the same notebook.
|
||||
|
||||
#!csharp
|
||||
|
||||
using static System.Console;
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
#!markdown
|
||||
|
||||
## Storing a value using an enum type (defining the enum)
|
||||
|
||||
#!csharp
|
||||
|
||||
[System.Flags]
|
||||
public enum WondersOfTheAncientWorld : byte
|
||||
{
|
||||
None = 0b_0000_0000, // i.e. 0
|
||||
GreatPyramidOfGiza = 0b_0000_0001, // i.e. 1
|
||||
HangingGardensOfBabylon = 0b_0000_0010, // i.e. 2
|
||||
StatueOfZeusAtOlympia = 0b_0000_0100, // i.e. 4
|
||||
TempleOfArtemisAtEphesus = 0b_0000_1000, // i.e. 8
|
||||
MausoleumAtHalicarnassus = 0b_0001_0000, // i.e. 16
|
||||
ColossusOfRhodes = 0b_0010_0000, // i.e. 32
|
||||
LighthouseOfAlexandria = 0b_0100_0000 // i.e. 64
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Defining a class
|
||||
|
||||
.NET Interactive notebooks do not support namespaces. You would get a "Cannot declare namesapce in script code" error. But we can declare a class.
|
||||
|
||||
#!markdown
|
||||
|
||||
## Storing a value using an enum type (using the enum)
|
||||
|
||||
#!csharp
|
||||
|
||||
public class Person : object
|
||||
{
|
||||
// fields
|
||||
public string Name;
|
||||
public DateTime DateOfBirth;
|
||||
public WondersOfTheAncientWorld FavoriteAncientWonder;
|
||||
public WondersOfTheAncientWorld BucketList;
|
||||
public List<Person> Children = new();
|
||||
|
||||
// constants
|
||||
public const string Species = "Homo Sapien";
|
||||
|
||||
// read-only fields
|
||||
public readonly string HomePlanet = "Earth";
|
||||
public readonly DateTime Instantiated;
|
||||
|
||||
// constructors
|
||||
public Person()
|
||||
{
|
||||
// set default values for fields
|
||||
// including read-only fields
|
||||
Name = "Unknown";
|
||||
Instantiated = DateTime.Now;
|
||||
}
|
||||
|
||||
public Person(string initialName, string homePlanet)
|
||||
{
|
||||
Name = initialName;
|
||||
HomePlanet = homePlanet;
|
||||
Instantiated = DateTime.Now;
|
||||
}
|
||||
|
||||
// methods
|
||||
public void WriteToConsole()
|
||||
{
|
||||
WriteLine($"{Name} was born on a {DateOfBirth:dddd}.");
|
||||
}
|
||||
|
||||
public string GetOrigin()
|
||||
{
|
||||
return $"{Name} was born on {HomePlanet}.";
|
||||
}
|
||||
|
||||
public (string, int) GetFruit()
|
||||
{
|
||||
return ("Apples", 5);
|
||||
}
|
||||
|
||||
public (string Name, int Number) GetNamedFruit()
|
||||
{
|
||||
return (Name: "Apples", Number: 5);
|
||||
}
|
||||
|
||||
public string SayHello()
|
||||
{
|
||||
return $"{Name} says 'Hello!'";
|
||||
}
|
||||
|
||||
public string SayHello(string name)
|
||||
{
|
||||
return $"{Name} says 'Hello {name}!'";
|
||||
}
|
||||
|
||||
public string OptionalParameters(
|
||||
string command = "Run!",
|
||||
double number = 0.0,
|
||||
bool active = true)
|
||||
{
|
||||
return string.Format(
|
||||
format: "command is {0}, number is {1}, active is {2}",
|
||||
arg0: command,
|
||||
arg1: number,
|
||||
arg2: active);
|
||||
}
|
||||
|
||||
public void PassingParameters(int x, ref int y, out int z)
|
||||
{
|
||||
// out parameters cannot have a default
|
||||
// AND must be initialized inside the method
|
||||
z = 99;
|
||||
// increment each parameter
|
||||
x++;
|
||||
y++;
|
||||
z++;
|
||||
}
|
||||
|
||||
// a property defined using C# 1 - 5 syntax
|
||||
public string Origin
|
||||
{
|
||||
get
|
||||
{
|
||||
return $"{Name} was born on {HomePlanet}";
|
||||
}
|
||||
}
|
||||
|
||||
// two properties defined using C# 6+ lambda expression body syntax
|
||||
public string Greeting => $"{Name} says 'Hello!'";
|
||||
|
||||
public int Age => System.DateTime.Today.Year - DateOfBirth.Year;
|
||||
|
||||
public string FavoriteIceCream { get; set; } // auto-syntax
|
||||
|
||||
private string favoritePrimaryColor;
|
||||
public string FavoritePrimaryColor
|
||||
{
|
||||
get
|
||||
{
|
||||
return favoritePrimaryColor;
|
||||
}
|
||||
set
|
||||
{
|
||||
switch (value.ToLower())
|
||||
{
|
||||
case "red":
|
||||
case "green":
|
||||
case "blue":
|
||||
favoritePrimaryColor = value;
|
||||
break;
|
||||
default:
|
||||
throw new System.ArgumentException(
|
||||
$"{value} is not a primary color. " +
|
||||
"Choose from: red, green, blue.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// indexers
|
||||
public Person this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return Children[index]; // pass on to the List<T> indexer
|
||||
}
|
||||
set
|
||||
{
|
||||
Children[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// end of class
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Instantiating a class
|
||||
|
||||
The namespace for a class defined in a .NET Interactive notebook follows the pattern `Submission#[number]+[classname]`.
|
||||
|
||||
#!csharp
|
||||
|
||||
// Person bob = new Person(); // C# 1.0 or later
|
||||
// var bob = new Person(); // C# 3.0 or later
|
||||
Person bob = new(); // C# 9.0 or later
|
||||
WriteLine(bob.ToString());
|
||||
|
||||
bob.Name = "Bob Smith";
|
||||
bob.DateOfBirth = new DateTime(1965, 12, 22); // C# 1.0 or later
|
||||
|
||||
WriteLine(format: "{0} was born on {1:dddd, d MMMM yyyy}",
|
||||
arg0: bob.Name,
|
||||
arg1: bob.DateOfBirth);
|
||||
|
||||
Person alice = new()
|
||||
{
|
||||
Name = "Alice Jones",
|
||||
DateOfBirth = new(1998, 3, 7) // C# 9.0 or later
|
||||
};
|
||||
|
||||
WriteLine(format: "{0} was born on {1:dd MMM yy}",
|
||||
arg0: alice.Name,
|
||||
arg1: alice.DateOfBirth);
|
||||
|
||||
#!csharp
|
||||
|
||||
bob.FavoriteAncientWonder = WondersOfTheAncientWorld.StatueOfZeusAtOlympia;
|
||||
|
||||
WriteLine(
|
||||
format: "{0}'s favorite wonder is {1}. Its integer is {2}.",
|
||||
arg0: bob.Name,
|
||||
arg1: bob.FavoriteAncientWonder,
|
||||
arg2: (int)bob.FavoriteAncientWonder);
|
||||
|
||||
bob.BucketList =
|
||||
WondersOfTheAncientWorld.HangingGardensOfBabylon
|
||||
| WondersOfTheAncientWorld.MausoleumAtHalicarnassus;
|
||||
|
||||
// bob.BucketList = (WondersOfTheAncientWorld)18;
|
||||
|
||||
WriteLine($"{bob.Name}'s bucket list is {bob.BucketList}");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Storing multiple values using collections
|
||||
|
||||
#!csharp
|
||||
|
||||
bob.Children.Add(new Person { Name = "Alfred" });
|
||||
bob.Children.Add(new Person { Name = "Zoe" });
|
||||
|
||||
WriteLine(
|
||||
$"{bob.Name} has {bob.Children.Count} children:");
|
||||
|
||||
for (int childIndex = 0; childIndex < bob.Children.Count; childIndex++)
|
||||
{
|
||||
WriteLine($" {bob.Children[childIndex].Name}");
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Making a field static
|
||||
|
||||
#!csharp
|
||||
|
||||
public class BankAccount
|
||||
{
|
||||
public string AccountName; // instance member
|
||||
public decimal Balance; // instance member
|
||||
public static decimal InterestRate; // shared member
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
BankAccount.InterestRate = 0.012M; // store a shared value
|
||||
|
||||
var jonesAccount = new BankAccount();
|
||||
jonesAccount.AccountName = "Mrs. Jones";
|
||||
jonesAccount.Balance = 2400;
|
||||
|
||||
WriteLine(format: "{0} earned {1:C} interest.",
|
||||
arg0: jonesAccount.AccountName,
|
||||
arg1: jonesAccount.Balance * BankAccount.InterestRate);
|
||||
|
||||
var gerrierAccount = new BankAccount();
|
||||
gerrierAccount.AccountName = "Ms. Gerrier";
|
||||
gerrierAccount.Balance = 98;
|
||||
|
||||
WriteLine(format: "{0} earned {1:C} interest.",
|
||||
arg0: gerrierAccount.AccountName,
|
||||
arg1: gerrierAccount.Balance * BankAccount.InterestRate);
|
||||
|
||||
#!markdown
|
||||
|
||||
## Making a field constant
|
||||
|
||||
#!csharp
|
||||
|
||||
WriteLine($"{bob.Name} is a {Person.Species}");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Making a field read-only
|
||||
|
||||
#!csharp
|
||||
|
||||
WriteLine($"{bob.Name} was born on {bob.HomePlanet}");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Initializing fields with constructors
|
||||
|
||||
#!csharp
|
||||
|
||||
var blankPerson = new Person();
|
||||
|
||||
WriteLine(format:
|
||||
"{0} of {1} was created at {2:hh:mm:ss} on a {2:dddd}.",
|
||||
arg0: blankPerson.Name,
|
||||
arg1: blankPerson.HomePlanet,
|
||||
arg2: blankPerson.Instantiated);
|
||||
|
||||
#!markdown
|
||||
|
||||
## Defining multiple constructors
|
||||
|
||||
#!csharp
|
||||
|
||||
var gunny = new Person("Gunny", "Mars");
|
||||
|
||||
WriteLine(format:
|
||||
"{0} of {1} was created at {2:hh:mm:ss} on a {2:dddd}.",
|
||||
arg0: gunny.Name,
|
||||
arg1: gunny.HomePlanet,
|
||||
arg2: gunny.Instantiated);
|
||||
|
||||
#!markdown
|
||||
|
||||
## Returning values from methods
|
||||
|
||||
#!csharp
|
||||
|
||||
bob.WriteToConsole();
|
||||
WriteLine(bob.GetOrigin());
|
||||
|
||||
#!markdown
|
||||
|
||||
## Combining multiple returned values using tuples
|
||||
|
||||
#!csharp
|
||||
|
||||
public class TextAndNumber
|
||||
{
|
||||
public string Text;
|
||||
public int Number;
|
||||
}
|
||||
|
||||
public class LifeTheUniverseAndEverything
|
||||
{
|
||||
public TextAndNumber GetTheData()
|
||||
{
|
||||
return new TextAndNumber
|
||||
{
|
||||
Text = "What's the meaning of life?",
|
||||
Number = 42
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
### Language support for tuples
|
||||
|
||||
#!csharp
|
||||
|
||||
(string, int) fruit = bob.GetFruit();
|
||||
|
||||
WriteLine($"{fruit.Item1}, {fruit.Item2} there are.");
|
||||
|
||||
#!markdown
|
||||
|
||||
### Naming the fields of a tuple
|
||||
|
||||
#!csharp
|
||||
|
||||
var fruitNamed = bob.GetNamedFruit();
|
||||
|
||||
WriteLine($"There are {fruitNamed.Number} {fruitNamed.Name}.");
|
||||
|
||||
#!markdown
|
||||
|
||||
### Inferring tuple names
|
||||
|
||||
#!csharp
|
||||
|
||||
var thing1 = ("Neville", 4);
|
||||
WriteLine($"{thing1.Item1} has {thing1.Item2} children.");
|
||||
|
||||
var thing2 = (bob.Name, bob.Children.Count);
|
||||
WriteLine($"{thing2.Name} has {thing2.Count} children.");
|
||||
|
||||
#!markdown
|
||||
|
||||
### Deconstructing tuples
|
||||
|
||||
#!csharp
|
||||
|
||||
(string fruitName, int fruitNumber) = bob.GetFruit();
|
||||
|
||||
WriteLine($"Deconstructed: {fruitName}, {fruitNumber}");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Defining and passing parameters to methods
|
||||
|
||||
#!csharp
|
||||
|
||||
WriteLine(bob.SayHello());
|
||||
WriteLine(bob.SayHello("Emily"));
|
||||
|
||||
#!markdown
|
||||
|
||||
## Passing optional and named parameters
|
||||
|
||||
#!csharp
|
||||
|
||||
WriteLine(bob.OptionalParameters());
|
||||
|
||||
#!csharp
|
||||
|
||||
WriteLine(bob.OptionalParameters("Jump!", 98.5));
|
||||
|
||||
#!csharp
|
||||
|
||||
WriteLine(bob.OptionalParameters(
|
||||
number: 52.7, command: "Hide!"));
|
||||
|
||||
#!csharp
|
||||
|
||||
WriteLine(bob.OptionalParameters("Poke!", active: false));
|
||||
|
||||
#!markdown
|
||||
|
||||
## Controlling how parameters are passed
|
||||
|
||||
When a parameter is passed into a method, it can be passed in one of three ways:
|
||||
- By value (this is the default): Think of these as being in-only.
|
||||
- By reference as a ref parameter: Think of these as being in-and-out.
|
||||
- As an out parameter: Think of these as being out-only.
|
||||
|
||||
#!csharp
|
||||
|
||||
int a = 10;
|
||||
int b = 20;
|
||||
int c = 30;
|
||||
|
||||
WriteLine($"Before: a = {a}, b = {b}, c = {c}");
|
||||
bob.PassingParameters(a, ref b, out c);
|
||||
WriteLine($"After: a = {a}, b = {b}, c = {c}");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Simplified out parameters
|
||||
|
||||
In C# 7.0 and later, we can simplify code that uses the out variables.
|
||||
|
||||
#!csharp
|
||||
|
||||
int d = 10;
|
||||
int e = 20;
|
||||
|
||||
WriteLine($"Before: d = {d}, e = {e}, f doesn't exist yet!");
|
||||
|
||||
// simplified C# 7.0 or later syntax for the out parameter
|
||||
bob.PassingParameters(d, ref e, out int f);
|
||||
WriteLine($"After: d = {d}, e = {e}, f = {f}");
|
||||
|
||||
#!markdown
|
||||
|
||||
# Splitting classes using partial
|
||||
|
||||
Partial classes are not supported in .NET Interactive notebooks. I have added the code to the original class near the top of this notebook.
|
||||
|
||||
#!csharp
|
||||
|
||||
Person sam = new()
|
||||
{
|
||||
Name = "Sam",
|
||||
DateOfBirth = new(1972, 1, 27)
|
||||
};
|
||||
WriteLine(sam.Origin);
|
||||
WriteLine(sam.Greeting);
|
||||
WriteLine(sam.Age);
|
||||
|
||||
#!csharp
|
||||
|
||||
sam.FavoriteIceCream = "Chocolate Fudge";
|
||||
|
||||
WriteLine($"Sam's favorite ice-cream flavor is {sam.FavoriteIceCream}.");
|
||||
|
||||
sam.FavoritePrimaryColor = "Red";
|
||||
|
||||
WriteLine($"Sam's favorite primary color is {sam.FavoritePrimaryColor}.");
|
||||
|
||||
#!markdown
|
||||
|
||||
# Defining indexers
|
||||
|
||||
#!csharp
|
||||
|
||||
sam.Children.Add(new() { Name = "Charlie" });
|
||||
sam.Children.Add(new() { Name = "Ella" });
|
||||
|
||||
WriteLine($"Sam's first child is {sam.Children[0].Name}");
|
||||
WriteLine($"Sam's second child is {sam.Children[1].Name}");
|
||||
|
||||
WriteLine($"Sam's first child is {sam[0].Name}");
|
||||
WriteLine($"Sam's second child is {sam[1].Name}");
|
||||
|
||||
#!markdown
|
||||
|
||||
# Pattern matching with objects
|
||||
|
||||
#!csharp
|
||||
|
||||
public class BusinessClassPassenger
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Business Class";
|
||||
}
|
||||
}
|
||||
|
||||
public class FirstClassPassenger
|
||||
{
|
||||
public int AirMiles { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"First Class with {AirMiles:N0} air miles";
|
||||
}
|
||||
}
|
||||
|
||||
public class CoachClassPassenger
|
||||
{
|
||||
public double CarryOnKG { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Coach Class with {CarryOnKG:N2} KG carry on";
|
||||
}
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
object[] passengers = {
|
||||
new FirstClassPassenger { AirMiles = 1_419 },
|
||||
new FirstClassPassenger { AirMiles = 16_562 },
|
||||
new BusinessClassPassenger(),
|
||||
new CoachClassPassenger { CarryOnKG = 25.7 },
|
||||
new CoachClassPassenger { CarryOnKG = 0 },
|
||||
};
|
||||
|
||||
foreach (object passenger in passengers)
|
||||
{
|
||||
decimal flightCost = passenger switch
|
||||
{
|
||||
/*
|
||||
FirstClassPassenger p when p.AirMiles > 35000 => 1500M,
|
||||
FirstClassPassenger p when p.AirMiles > 15000 => 1750M,
|
||||
FirstClassPassenger _ => 2000M, */
|
||||
|
||||
// C# 9 or later syntax
|
||||
FirstClassPassenger p => p.AirMiles switch
|
||||
{
|
||||
> 35000 => 1500M,
|
||||
> 15000 => 1750M,
|
||||
_ => 2000M
|
||||
},
|
||||
|
||||
|
||||
BusinessClassPassenger => 1000M,
|
||||
CoachClassPassenger p when p.CarryOnKG < 10.0 => 500M,
|
||||
CoachClassPassenger => 650M,
|
||||
_ => 800M
|
||||
};
|
||||
|
||||
WriteLine($"Flight costs {flightCost:C} for {passenger}");
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
# Working with records
|
||||
|
||||
#!markdown
|
||||
|
||||
## Init-only properties
|
||||
|
||||
#!csharp
|
||||
|
||||
#nullable enable
|
||||
|
||||
public class ImmutablePerson
|
||||
{
|
||||
public string? FirstName { get; init; }
|
||||
public string? LastName { get; init; }
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
ImmutablePerson jeff = new()
|
||||
{
|
||||
FirstName = "Jeff",
|
||||
LastName = "Winger"
|
||||
};
|
||||
jeff.FirstName = "Geoff";
|
||||
|
||||
#!markdown
|
||||
|
||||
## Understanding records
|
||||
|
||||
#!csharp
|
||||
|
||||
#nullable enable
|
||||
|
||||
public record ImmutableVehicle
|
||||
{
|
||||
public int Wheels { get; init; }
|
||||
public string? Color { get; init; }
|
||||
public string? Brand { get; init; }
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
ImmutableVehicle car = new()
|
||||
{
|
||||
Brand = "Mazda MX-5 RF",
|
||||
Color = "Soul Red Crystal Metallic",
|
||||
Wheels = 4
|
||||
};
|
||||
|
||||
ImmutableVehicle repaintedCar = car
|
||||
with { Color = "Polymetal Grey Metallic" };
|
||||
|
||||
WriteLine($"Original car color was {car.Color}.");
|
||||
WriteLine($"New car color is {repaintedCar.Color}.");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Simplifying data members in records
|
||||
|
||||
#!csharp
|
||||
|
||||
// simpler way to define a record
|
||||
// auto-generates the properties, constructor, and deconstructor
|
||||
public record ImmutableAnimal(string Name, string Species);
|
||||
|
||||
#!csharp
|
||||
|
||||
ImmutableAnimal oscar = new("Oscar", "Labrador");
|
||||
var (who, what) = oscar; // calls Deconstruct method
|
||||
WriteLine($"{who} is a {what}.");
|
||||
860
notebooks/Chapter06.dib
Normal file
|
|
@ -0,0 +1,860 @@
|
|||
#!markdown
|
||||
|
||||
# Chapter 6 - Implementing Interfaces and Inheriting Classes
|
||||
|
||||
#!csharp
|
||||
|
||||
using static System.Console;
|
||||
|
||||
#!csharp
|
||||
|
||||
public class PersonException : Exception
|
||||
{
|
||||
public PersonException() : base() { }
|
||||
|
||||
public PersonException(string message) : base(message) { }
|
||||
|
||||
public PersonException(string message, Exception innerException)
|
||||
: base(message, innerException) { }
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
#nullable enable // required in notebook cells that use nullability
|
||||
|
||||
public class Person : object, IComparable<Person>
|
||||
{
|
||||
// fields
|
||||
public string? Name; // ? allows null
|
||||
public DateTime DateOfBirth;
|
||||
public List<Person> Children = new(); // C# 9 or later
|
||||
|
||||
// methods
|
||||
public void WriteToConsole()
|
||||
{
|
||||
WriteLine($"{Name} was born on a {DateOfBirth:dddd}.");
|
||||
}
|
||||
|
||||
// static method to "multiply"
|
||||
public static Person Procreate(Person p1, Person p2)
|
||||
{
|
||||
Person baby = new()
|
||||
{
|
||||
Name = $"Baby of {p1.Name} and {p2.Name}"
|
||||
};
|
||||
|
||||
p1.Children.Add(baby);
|
||||
p2.Children.Add(baby);
|
||||
|
||||
return baby;
|
||||
}
|
||||
|
||||
// instance method to "multiply"
|
||||
public Person ProcreateWith(Person partner)
|
||||
{
|
||||
return Procreate(this, partner);
|
||||
}
|
||||
|
||||
// operator to "multiply"
|
||||
public static Person operator *(Person p1, Person p2)
|
||||
{
|
||||
return Person.Procreate(p1, p2);
|
||||
}
|
||||
|
||||
// method with a local function
|
||||
public static int Factorial(int number)
|
||||
{
|
||||
if (number < 0)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"{nameof(number)} cannot be less than zero.");
|
||||
}
|
||||
return localFactorial(number);
|
||||
|
||||
int localFactorial(int localNumber) // local function
|
||||
{
|
||||
if (localNumber < 1) return 1;
|
||||
return localNumber * localFactorial(localNumber - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// delegate field
|
||||
public event EventHandler? Shout;
|
||||
|
||||
// data field
|
||||
public int AngerLevel;
|
||||
|
||||
// method
|
||||
public void Poke()
|
||||
{
|
||||
AngerLevel++;
|
||||
|
||||
if (AngerLevel >= 3)
|
||||
{
|
||||
// if something is listening...
|
||||
if (Shout != null)
|
||||
{
|
||||
// ...then call the delegate
|
||||
Shout(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int CompareTo(Person? other)
|
||||
{
|
||||
if (Name is null) return 0;
|
||||
return Name.CompareTo(other?.Name);
|
||||
}
|
||||
|
||||
// overridden methods
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Name} is a {base.ToString()}";
|
||||
}
|
||||
|
||||
public void TimeTravel(DateTime when)
|
||||
{
|
||||
if (when <= DateOfBirth)
|
||||
{
|
||||
throw new PersonException("If you travel back in time to a date earlier than your own birth, then the universe will explode!");
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLine($"Welcome to {when:yyyy}!");
|
||||
}
|
||||
}
|
||||
|
||||
// end of class
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
# Implementing functionality using methods
|
||||
|
||||
#!csharp
|
||||
|
||||
Person harry = new() { Name = "Harry" };
|
||||
Person mary = new() { Name = "Mary" };
|
||||
Person jill = new() { Name = "Jill" };
|
||||
|
||||
// call instance method
|
||||
Person baby1 = mary.ProcreateWith(harry);
|
||||
baby1.Name = "Gary";
|
||||
|
||||
// call static method
|
||||
Person baby2 = Person.Procreate(harry, jill);
|
||||
|
||||
// call an operator
|
||||
Person baby3 = harry * mary;
|
||||
|
||||
WriteLine($"{harry.Name} has {harry.Children.Count} children.");
|
||||
WriteLine($"{mary.Name} has {mary.Children.Count} children.");
|
||||
WriteLine($"{jill.Name} has {jill.Children.Count} children.");
|
||||
WriteLine(
|
||||
format: "{0}'s first child is named \"{1}\".",
|
||||
arg0: harry.Name,
|
||||
arg1: harry.Children[0].Name);
|
||||
|
||||
#!markdown
|
||||
|
||||
## Implementing functionality using local functions
|
||||
|
||||
#!csharp
|
||||
|
||||
WriteLine($"5! is {Person.Factorial(5)}");
|
||||
|
||||
#!markdown
|
||||
|
||||
# Raising and handling events
|
||||
|
||||
#!markdown
|
||||
|
||||
## Defining and handling delegates
|
||||
|
||||
#!csharp
|
||||
|
||||
#nullable enable
|
||||
|
||||
static void Harry_Shout(object? sender, EventArgs e)
|
||||
{
|
||||
if (sender is null) return;
|
||||
Person p = (Person)sender;
|
||||
WriteLine($"{p.Name} is this angry: {p.AngerLevel}.");
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
harry.Shout += Harry_Shout;
|
||||
|
||||
#!csharp
|
||||
|
||||
harry.Poke();
|
||||
harry.Poke();
|
||||
harry.Poke();
|
||||
harry.Poke();
|
||||
|
||||
#!markdown
|
||||
|
||||
# Making types safely reusable with generics
|
||||
|
||||
#!markdown
|
||||
|
||||
## Working with non-generic types
|
||||
|
||||
#!csharp
|
||||
|
||||
// non-generic lookup collection
|
||||
System.Collections.Hashtable lookupObject = new();
|
||||
|
||||
lookupObject.Add(key: 1, value: "Alpha");
|
||||
lookupObject.Add(key: 2, value: "Beta");
|
||||
lookupObject.Add(key: 3, value: "Gamma");
|
||||
lookupObject.Add(key: harry, value: "Delta");
|
||||
|
||||
int key = 2; // lookup the value that has 2 as its key
|
||||
WriteLine(format: "Key {0} has value: {1}",
|
||||
arg0: key,
|
||||
arg1: lookupObject[key]);
|
||||
|
||||
// lookup the value that has harry as its key
|
||||
WriteLine(format: "Key {0} has value: {1}",
|
||||
arg0: harry,
|
||||
arg1: lookupObject[harry]);
|
||||
|
||||
#!markdown
|
||||
|
||||
## Working with generic types
|
||||
|
||||
#!csharp
|
||||
|
||||
// generic lookup collection
|
||||
Dictionary<int, string> lookupIntString = new();
|
||||
|
||||
lookupIntString.Add(key: 1, value: "Alpha");
|
||||
lookupIntString.Add(key: 2, value: "Beta");
|
||||
lookupIntString.Add(key: 3, value: "Gamma");
|
||||
lookupIntString.Add(key: 4, value: "Delta");
|
||||
|
||||
key = 3;
|
||||
WriteLine(format: "Key {0} has value: {1}",
|
||||
arg0: key,
|
||||
arg1: lookupIntString[key]);
|
||||
|
||||
#!markdown
|
||||
|
||||
# Implementing interfaces
|
||||
|
||||
#!markdown
|
||||
|
||||
## Comparing objects when sorting
|
||||
|
||||
#!csharp
|
||||
|
||||
Person[] people =
|
||||
{
|
||||
new() { Name = "Simon" },
|
||||
new() { Name = "Jenny" },
|
||||
new() { Name = "Adam" },
|
||||
new() { Name = "Richard" }
|
||||
};
|
||||
|
||||
WriteLine("Initial list of people:");
|
||||
foreach (Person p in people)
|
||||
{
|
||||
WriteLine($" {p.Name}");
|
||||
}
|
||||
|
||||
WriteLine("Use Person's IComparable implementation to sort:");
|
||||
Array.Sort(people);
|
||||
foreach (Person p in people)
|
||||
{
|
||||
WriteLine($" {p.Name}");
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Comparing objects using a separate class
|
||||
|
||||
#!csharp
|
||||
|
||||
#nullable enable
|
||||
|
||||
public class PersonComparer : IComparer<Person>
|
||||
{
|
||||
public int Compare(Person? x, Person? y)
|
||||
{
|
||||
if (x is null || y is null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Compare the Name lengths...
|
||||
int result = x.Name.Length.CompareTo(y.Name.Length);
|
||||
|
||||
// ...if they are equal...
|
||||
if (result == 0)
|
||||
{
|
||||
// ...then compare by the Names...
|
||||
return x.Name.CompareTo(y.Name);
|
||||
}
|
||||
else // result will be -1 or 1
|
||||
{
|
||||
// ...otherwise compare by the lengths.
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
WriteLine("Use PersonComparer's IComparer implementation to sort:");
|
||||
Array.Sort(people, new PersonComparer());
|
||||
foreach (Person p in people)
|
||||
{
|
||||
WriteLine($" {p.Name}");
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Implicit and explicit interface implementations
|
||||
|
||||
#!csharp
|
||||
|
||||
public interface IGamePlayer
|
||||
{
|
||||
void Lose();
|
||||
}
|
||||
|
||||
public interface IKeyHolder
|
||||
{
|
||||
void Lose();
|
||||
}
|
||||
|
||||
public class Human : IGamePlayer, IKeyHolder
|
||||
{
|
||||
public void Lose() // implicit implementation
|
||||
{
|
||||
// implement losing a key
|
||||
WriteLine("Lost my key!");
|
||||
}
|
||||
void IGamePlayer.Lose() // explicit implementation
|
||||
{
|
||||
// implement losing a game
|
||||
WriteLine("Lost the game!");
|
||||
}
|
||||
}
|
||||
|
||||
// calling implicit and explicit implementations of Lose
|
||||
Human p = new();
|
||||
|
||||
p.Lose(); // calls implicit implementation of losing a key
|
||||
|
||||
((IGamePlayer)p).Lose(); // calls explicit implementation of losing a game
|
||||
|
||||
IGamePlayer player = p as IGamePlayer;
|
||||
|
||||
player.Lose(); // calls explicit implementation of losing a game
|
||||
|
||||
#!markdown
|
||||
|
||||
## Defining interfaces with default implementations
|
||||
|
||||
Note that the code cell below executes successfully despite the `DvdPlayer` class not implementing `Stop`.
|
||||
|
||||
#!csharp
|
||||
|
||||
public interface IPlayable
|
||||
{
|
||||
void Play();
|
||||
void Pause();
|
||||
|
||||
void Stop() // default interface implementation
|
||||
{
|
||||
WriteLine("Default implementation of Stop.");
|
||||
}
|
||||
}
|
||||
|
||||
public class DvdPlayer : IPlayable
|
||||
{
|
||||
public void Pause()
|
||||
{
|
||||
WriteLine("DVD player is pausing.");
|
||||
}
|
||||
public void Play()
|
||||
{
|
||||
WriteLine("DVD player is playing.");
|
||||
}
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
# Managing memory with reference and value types
|
||||
|
||||
#!markdown
|
||||
|
||||
## How reference and value types are stored in memory
|
||||
|
||||
#!csharp
|
||||
|
||||
int number1 = 49;
|
||||
long number2 = 12;
|
||||
System.Drawing.Point location = new(x: 4, y: 5);
|
||||
|
||||
Person kevin = new() { Name = "Kevin",
|
||||
DateOfBirth = new(year: 1988, month: 9, day: 23) };
|
||||
|
||||
Person sally;
|
||||
|
||||
#!markdown
|
||||
|
||||
## Equality of types
|
||||
|
||||
#!csharp
|
||||
|
||||
int a = 3;
|
||||
int b = 3;
|
||||
WriteLine($"a == b: {(a == b)}"); // true
|
||||
|
||||
#!csharp
|
||||
|
||||
Person2 a = new() { Name = "Kevin" };
|
||||
Person2 b = new() { Name = "Kevin" };
|
||||
WriteLine($"a == b: {(a == b)}"); // false
|
||||
|
||||
#!csharp
|
||||
|
||||
Person2 a = new() { Name = "Kevin" };
|
||||
Person2 b = a;
|
||||
WriteLine($"a == b: {(a == b)}"); // true
|
||||
|
||||
#!csharp
|
||||
|
||||
string a = "Kevin";
|
||||
string b = "Kevin";
|
||||
WriteLine($"a == b: {(a == b)}"); // true
|
||||
|
||||
#!markdown
|
||||
|
||||
# Defining struct types
|
||||
|
||||
#!csharp
|
||||
|
||||
public struct DisplacementVector
|
||||
{
|
||||
public int X;
|
||||
public int Y;
|
||||
|
||||
public DisplacementVector(int initialX, int initialY)
|
||||
{
|
||||
X = initialX;
|
||||
Y = initialY;
|
||||
}
|
||||
|
||||
public static DisplacementVector operator +(
|
||||
DisplacementVector vector1,
|
||||
DisplacementVector vector2)
|
||||
{
|
||||
return new(
|
||||
vector1.X + vector2.X,
|
||||
vector1.Y + vector2.Y);
|
||||
}
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
DisplacementVector dv1 = new(3, 5);
|
||||
DisplacementVector dv2 = new(-2, 7);
|
||||
DisplacementVector dv3 = dv1 + dv2;
|
||||
|
||||
WriteLine($"({dv1.X}, {dv1.Y}) + ({dv2.X}, {dv2.Y}) = ({dv3.X}, {dv3.Y})");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Working with record struct types
|
||||
|
||||
#!csharp
|
||||
|
||||
public record struct DisplacementVector(int X, int Y);
|
||||
|
||||
#!markdown
|
||||
|
||||
## Releasing unmanaged resources
|
||||
|
||||
#!csharp
|
||||
|
||||
public class Animal
|
||||
{
|
||||
public Animal() // constructor
|
||||
{
|
||||
// allocate any unmanaged resources
|
||||
}
|
||||
|
||||
~Animal() // Finalizer aka destructor
|
||||
{
|
||||
// deallocate any unmanaged resources
|
||||
}
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
public class Animal : IDisposable
|
||||
{
|
||||
public Animal()
|
||||
{
|
||||
// allocate unmanaged resource
|
||||
}
|
||||
|
||||
~Animal() // Finalizer
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
bool disposed = false; // have resources been released?
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
// tell garbage collector it does not need to call the finalizer
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposed) return;
|
||||
|
||||
// deallocate the *unmanaged* resource
|
||||
// ...
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
// deallocate any other *managed* resources
|
||||
// ...
|
||||
}
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
using (Animal a = new())
|
||||
{
|
||||
// code that uses the Animal instance
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
Animal a = new();
|
||||
try
|
||||
{
|
||||
// code that uses the Animal instance
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (a != null) a.Dispose();
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
# Working with null values
|
||||
|
||||
#!csharp
|
||||
|
||||
int thisCannotBeNull = 4;
|
||||
//thisCannotBeNull = null; // compile error!
|
||||
|
||||
int? thisCouldBeNull = null;
|
||||
WriteLine(thisCouldBeNull);
|
||||
WriteLine(thisCouldBeNull.GetValueOrDefault());
|
||||
|
||||
thisCouldBeNull = 7;
|
||||
WriteLine(thisCouldBeNull);
|
||||
WriteLine(thisCouldBeNull.GetValueOrDefault());
|
||||
|
||||
#!markdown
|
||||
|
||||
## Declaring non-nullable variables and parameters
|
||||
|
||||
#!csharp
|
||||
|
||||
#nullable enable
|
||||
|
||||
class Address
|
||||
{
|
||||
public string? Building;
|
||||
public string Street = string.Empty;
|
||||
public string City = string.Empty;
|
||||
public string Region = string.Empty;
|
||||
}
|
||||
|
||||
Address address = new();
|
||||
address.Building = null;
|
||||
address.Street = null;
|
||||
address.City = "London";
|
||||
address.Region = null;
|
||||
|
||||
#!markdown
|
||||
|
||||
## Checking for null
|
||||
|
||||
#!csharp
|
||||
|
||||
string authorName = null;
|
||||
|
||||
// the following throws a NullReferenceException
|
||||
//int x = authorName.Length;
|
||||
|
||||
// instead of throwing an exception, null is assigned to y
|
||||
int? y = authorName?.Length;
|
||||
|
||||
if (!y.HasValue) WriteLine("y is null");
|
||||
|
||||
// result will be 3 if authorName?.Length is null
|
||||
int result = authorName?.Length ?? 3;
|
||||
Console.WriteLine(result);
|
||||
|
||||
#!markdown
|
||||
|
||||
# Inheriting from classes
|
||||
|
||||
#!csharp
|
||||
|
||||
#nullable enable
|
||||
|
||||
public class Employee : Person
|
||||
{
|
||||
public string? EmployeeCode { get; set; }
|
||||
public DateTime HireDate { get; set; }
|
||||
|
||||
public new void WriteToConsole()
|
||||
{
|
||||
WriteLine(format:
|
||||
"{0} was born on {1:dd/MM/yy} and hired on {2:dd/MM/yy}",
|
||||
arg0: Name,
|
||||
arg1: DateOfBirth,
|
||||
arg2: HireDate);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Name}'s code is {EmployeeCode}";
|
||||
}
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
Employee john = new()
|
||||
{
|
||||
Name = "John Jones",
|
||||
DateOfBirth = new(year: 1990, month: 7, day: 28)
|
||||
};
|
||||
john.WriteToConsole();
|
||||
|
||||
#!markdown
|
||||
|
||||
## Extending classes to add functionality
|
||||
|
||||
#!csharp
|
||||
|
||||
john.EmployeeCode = "JJ001";
|
||||
john.HireDate = new(year: 2014, month: 11, day: 23);
|
||||
WriteLine($"{john.Name} was hired on {john.HireDate:dd/MM/yy}");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Overriding members
|
||||
|
||||
#!csharp
|
||||
|
||||
WriteLine(john.ToString());
|
||||
|
||||
#!markdown
|
||||
|
||||
## Inheriting from abstract classes
|
||||
|
||||
#!csharp
|
||||
|
||||
public interface INoImplementation // C# 1.0 and later
|
||||
{
|
||||
void Alpha(); // must be implemented by derived type
|
||||
}
|
||||
|
||||
public interface ISomeImplementation // C# 8.0 and later
|
||||
{
|
||||
void Alpha(); // must be implemented by derived type
|
||||
|
||||
void Beta()
|
||||
{
|
||||
// default implementation; can be overridden
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class PartiallyImplemented // C# 1.0 and later
|
||||
{
|
||||
public abstract void Gamma(); // must be implemented by derived type
|
||||
|
||||
public virtual void Delta() // can be overridden
|
||||
{
|
||||
// implementation
|
||||
}
|
||||
}
|
||||
|
||||
public class FullyImplemented : PartiallyImplemented, ISomeImplementation
|
||||
{
|
||||
public void Alpha()
|
||||
{
|
||||
// implementation
|
||||
}
|
||||
public override void Gamma()
|
||||
{
|
||||
// implementation
|
||||
}
|
||||
}
|
||||
|
||||
// you can only instantiate the fully implemented class
|
||||
FullyImplemented a = new();
|
||||
|
||||
// all the other types give compile errors
|
||||
PartiallyImplemented b = new(); // compile error!
|
||||
ISomeImplementation c = new(); // compile error!
|
||||
INoImplementation d = new(); // compile error!
|
||||
|
||||
#!markdown
|
||||
|
||||
## Preventing inheritance and overriding
|
||||
|
||||
You can prevent another developer from inheriting from your class by applying the `sealed`
|
||||
keyword to its definition.
|
||||
|
||||
#!csharp
|
||||
|
||||
public sealed class ScroogeMcDuck
|
||||
{
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
You can prevent someone from further overriding a `virtual` method in your class by applying
|
||||
the `sealed` keyword to the method. You can only seal an overridden method.
|
||||
|
||||
#!csharp
|
||||
|
||||
public class Singer
|
||||
{
|
||||
// virtual allows this method to be overridden
|
||||
public virtual void Sing()
|
||||
{
|
||||
WriteLine("Singing...");
|
||||
}
|
||||
}
|
||||
|
||||
public class LadyGaga : Singer
|
||||
{
|
||||
// sealed prevents overriding the method in subclasses
|
||||
public sealed override void Sing()
|
||||
{
|
||||
WriteLine("Singing with style...");
|
||||
}
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Understanding polymorphism
|
||||
|
||||
#!csharp
|
||||
|
||||
Employee aliceInEmployee = new()
|
||||
{ Name = "Alice", EmployeeCode = "AA123" };
|
||||
|
||||
Person aliceInPerson = aliceInEmployee;
|
||||
aliceInEmployee.WriteToConsole();
|
||||
aliceInPerson.WriteToConsole();
|
||||
WriteLine(aliceInEmployee.ToString());
|
||||
WriteLine(aliceInPerson.ToString());
|
||||
|
||||
#!markdown
|
||||
|
||||
## Casting within inheritance hierarchies
|
||||
|
||||
#!csharp
|
||||
|
||||
Employee explicitAlice = aliceInPerson;
|
||||
|
||||
#!csharp
|
||||
|
||||
if (aliceInPerson is Employee)
|
||||
{
|
||||
WriteLine($"{nameof(aliceInPerson)} IS an Employee");
|
||||
Employee explicitAlice = (Employee)aliceInPerson;
|
||||
// safely do something with explicitAlice
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
#nullable enable
|
||||
|
||||
Employee? aliceAsEmployee = aliceInPerson as Employee; // could be null
|
||||
if (aliceAsEmployee != null)
|
||||
{
|
||||
WriteLine($"{nameof(aliceInPerson)} AS an Employee");
|
||||
// safely do something with aliceAsEmployee
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
# Inheriting and extending .NET types
|
||||
|
||||
#!csharp
|
||||
|
||||
try
|
||||
{
|
||||
john.TimeTravel(when: new(1999, 12, 31));
|
||||
john.TimeTravel(when: new(1950, 12, 25));
|
||||
}
|
||||
catch (PersonException ex)
|
||||
{
|
||||
WriteLine(ex.Message);
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Using static methods to reuse functionality
|
||||
|
||||
#!csharp
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
#!csharp
|
||||
|
||||
public class StringExtensions
|
||||
{
|
||||
public static bool IsValidEmail(string input)
|
||||
{
|
||||
// use simple regular expression to check
|
||||
// that the input string is a valid email
|
||||
return Regex.IsMatch(input,
|
||||
@"[a-zA-Z0-9\.-_]+@[a-zA-Z0-9\.-_]+");
|
||||
}
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
string email1 = "pamela@test.com";
|
||||
string email2 = "ian&test.com";
|
||||
|
||||
WriteLine("{0} is a valid e-mail address: {1}",
|
||||
arg0: email1,
|
||||
arg1: StringExtensions.IsValidEmail(email1));
|
||||
|
||||
WriteLine("{0} is a valid e-mail address: {1}",
|
||||
arg0: email2,
|
||||
arg1: StringExtensions.IsValidEmail(email2));
|
||||
|
||||
#!markdown
|
||||
|
||||
Extension methods must be defined in a top-level static method so they cannot be defined in a notebook.
|
||||
|
||||
#!markdown
|
||||
|
||||
# Using an analyzer to write better code
|
||||
|
||||
You cannot run code analyzers on notebook code.
|
||||
80
notebooks/Chapter07.dib
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
#!markdown
|
||||
|
||||
# Chapter 7 - Packaging and Distributing .NET Types
|
||||
|
||||
Most of this chapter is about creating apps and class libraries for deployment so .NET Interactive notebooks cannot be used for most code examples.
|
||||
|
||||
#!markdown
|
||||
|
||||
## Importing a namespace to use a type
|
||||
|
||||
#!csharp
|
||||
|
||||
using System.Xml.Linq; // XDocument
|
||||
|
||||
#!csharp
|
||||
|
||||
XDocument doc = new();
|
||||
|
||||
#!markdown
|
||||
|
||||
## Relating C# keywords to .NET types
|
||||
|
||||
One of the common questions I get from new C# programmers is, "What is the difference
|
||||
between `string` with a lowercase s and `String` with an uppercase S?"
|
||||
|
||||
#!csharp
|
||||
|
||||
using System; // String
|
||||
|
||||
#!csharp
|
||||
|
||||
string s1 = "Hello";
|
||||
String s2 = "World";
|
||||
|
||||
#!markdown
|
||||
|
||||
## Understanding native-sized integers
|
||||
|
||||
C# 9 introduced `nint` and `nuint` keyword alias for native-sized integers, meaning that the
|
||||
storage size for the integer value is platform specific.
|
||||
|
||||
#!csharp
|
||||
|
||||
using static System.Console;
|
||||
|
||||
#!csharp
|
||||
|
||||
WriteLine($"int.MaxValue = {int.MaxValue:N0}");
|
||||
WriteLine($"nint.MaxValue = {nint.MaxValue:N0}");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Testing your class library package
|
||||
|
||||
#!csharp
|
||||
|
||||
#r "nuget:packt.csdotnet.sharedlibrary,6.0.0"
|
||||
|
||||
#!csharp
|
||||
|
||||
using Packt.Shared;
|
||||
|
||||
#!csharp
|
||||
|
||||
#nullable enable
|
||||
|
||||
Write("Enter a color value in hex: ");
|
||||
string? hex = "00ffc8"; // or ReadLine();
|
||||
WriteLine("Is {0} a valid color value? {1}",
|
||||
arg0: hex, arg1: hex.IsValidHex());
|
||||
|
||||
Write("Enter a XML element: ");
|
||||
string? xmlTag = "<h1 class=\"<\" />"; // or ReadLine()
|
||||
WriteLine("Is {0} a valid XML element? {1}",
|
||||
arg0: xmlTag, arg1: xmlTag.IsValidXmlTag());
|
||||
|
||||
Write("Enter a password: ");
|
||||
string? password = "secretsauce"; // or ReadLine()
|
||||
WriteLine("Is {0} a valid password? {1}",
|
||||
arg0: password, arg1: password.IsValidPassword());
|
||||
878
notebooks/Chapter08.dib
Normal file
|
|
@ -0,0 +1,878 @@
|
|||
#!markdown
|
||||
|
||||
# Chapter 8 - Working with Common .NET Types
|
||||
|
||||
Execute the following code cell to make `Console` methods available in every code cell in this notebook.
|
||||
|
||||
#!csharp
|
||||
|
||||
using static System.Console;
|
||||
|
||||
#!markdown
|
||||
|
||||
# Working with numbers
|
||||
|
||||
One of the most common types of data is numbers.
|
||||
|
||||
#!csharp
|
||||
|
||||
using System.Numerics;
|
||||
|
||||
#!markdown
|
||||
|
||||
## Working with big integers
|
||||
|
||||
#!csharp
|
||||
|
||||
WriteLine("Working with large integers:");
|
||||
WriteLine("----------------------------------------");
|
||||
ulong big = ulong.MaxValue;
|
||||
WriteLine($"{big,40:N0}");
|
||||
BigInteger bigger =
|
||||
BigInteger.Parse("123456789012345678901234567890");
|
||||
WriteLine($"{bigger,40:N0}");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Working with complex numbers
|
||||
|
||||
#!csharp
|
||||
|
||||
WriteLine("Working with complex numbers:");
|
||||
Complex c1 = new(real: 4, imaginary: 2);
|
||||
Complex c2 = new(real: 3, imaginary: 7);
|
||||
Complex c3 = c1 + c2;
|
||||
// output using default ToString implementation
|
||||
WriteLine($"{c1} added to {c2} is {c3}"); // output using custom format
|
||||
WriteLine("{0} + {1}i added to {2} + {3}i is {4} + {5}i",
|
||||
c1.Real, c1.Imaginary,
|
||||
c2.Real, c2.Imaginary,
|
||||
c3.Real, c3.Imaginary);
|
||||
|
||||
#!markdown
|
||||
|
||||
# Working with text
|
||||
|
||||
One of the other most common types of data for variables is text.
|
||||
|
||||
#!csharp
|
||||
|
||||
string city = "London";
|
||||
|
||||
#!markdown
|
||||
|
||||
## Getting the length of a string
|
||||
|
||||
#!csharp
|
||||
|
||||
WriteLine($"{city} is {city.Length} characters long.");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Getting the characters of a string
|
||||
|
||||
#!csharp
|
||||
|
||||
WriteLine($"First char is {city[0]} and third is {city[2]}.");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Splitting a string
|
||||
|
||||
#!csharp
|
||||
|
||||
string cities = "Paris,Tehran,Chennai,Sydney,New York,Medellín";
|
||||
string[] citiesArray = cities.Split(',');
|
||||
WriteLine($"There are {citiesArray.Length} items in the array.");
|
||||
foreach (string item in citiesArray)
|
||||
{
|
||||
WriteLine(item);
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Getting part of a string
|
||||
|
||||
Sometimes, you need to get part of some text. The `IndexOf` method has nine overloads that return the index position of a specified `char` or `string` within a `string`. The `Substring` method has two overloads, as shown in the following list:
|
||||
- `Substring(startIndex, length)`: returns a substring starting at startIndex and containing the next length characters.
|
||||
- `Substring(startIndex)`: returns a substring starting at startIndex and containing all characters up to the end of the string.
|
||||
|
||||
#!csharp
|
||||
|
||||
string fullName = "Alan Jones";
|
||||
int indexOfTheSpace = fullName.IndexOf(' ');
|
||||
|
||||
string firstName = fullName.Substring(
|
||||
startIndex: 0, length: indexOfTheSpace);
|
||||
|
||||
string lastName = fullName.Substring(
|
||||
startIndex: indexOfTheSpace + 1);
|
||||
|
||||
WriteLine($"Original: {fullName}");
|
||||
WriteLine($"Swapped: {lastName}, {firstName}");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Checking a string for content
|
||||
|
||||
Sometimes, you need to check whether a piece of text starts or ends with some characters or contains some characters. You can achieve this with methods named `StartsWith`, `EndsWith`, and `Contains`:
|
||||
|
||||
#!csharp
|
||||
|
||||
string company = "Microsoft";
|
||||
bool startsWithM = company.StartsWith("M");
|
||||
bool containsN = company.Contains("N");
|
||||
WriteLine($"Text: {company}");
|
||||
WriteLine($"Starts with M: {startsWithM}, contains an N: {containsN}");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Joining, formatting, and other string members
|
||||
|
||||
#!csharp
|
||||
|
||||
string recombined = string.Join(" => ", citiesArray);
|
||||
WriteLine(recombined);
|
||||
|
||||
#!csharp
|
||||
|
||||
string fruit = "Apples";
|
||||
decimal price = 0.39M;
|
||||
DateTime when = DateTime.Today;
|
||||
WriteLine($"Interpolated: {fruit} cost {price:C} on {when:dddd}.");
|
||||
WriteLine(string.Format("string.Format: {0} cost {1:C} on {2:dddd}.",
|
||||
arg0: fruit, arg1: price, arg2: when));
|
||||
|
||||
#!markdown
|
||||
|
||||
# Working with dates and times
|
||||
|
||||
After numbers and text, the next most popular types of data to work with are dates and times.
|
||||
|
||||
#!markdown
|
||||
|
||||
## Specifying date and time values
|
||||
|
||||
#!csharp
|
||||
|
||||
WriteLine("Earliest date/time value is: {0}",
|
||||
arg0: DateTime.MinValue);
|
||||
WriteLine("UNIX epoch date/time value is: {0}",
|
||||
arg0: DateTime.UnixEpoch);
|
||||
WriteLine("Date/time value Now is: {0}",
|
||||
arg0: DateTime.Now);
|
||||
WriteLine("Date/time value Today is: {0}",
|
||||
arg0: DateTime.Today);
|
||||
|
||||
#!csharp
|
||||
|
||||
DateTime christmas = new(year: 2021, month: 12, day: 25);
|
||||
|
||||
WriteLine("Christmas: {0}",
|
||||
arg0: christmas); // default format
|
||||
|
||||
WriteLine("Christmas: {0:dddd, dd MMMM yyyy}",
|
||||
arg0: christmas); // custom format
|
||||
|
||||
WriteLine("Christmas is in month {0} of the year.",
|
||||
arg0: christmas.Month);
|
||||
|
||||
WriteLine("Christmas is day {0} of the year.",
|
||||
arg0: christmas.DayOfYear);
|
||||
|
||||
WriteLine("Christmas {0} is on a {1}.",
|
||||
arg0: christmas.Year,
|
||||
arg1: christmas.DayOfWeek);
|
||||
|
||||
#!csharp
|
||||
|
||||
DateTime beforeXmas = christmas.Subtract(TimeSpan.FromDays(12));
|
||||
DateTime afterXmas = christmas.AddDays(12);
|
||||
|
||||
WriteLine("12 days before Christmas is: {0}",
|
||||
arg0: beforeXmas);
|
||||
|
||||
WriteLine("12 days after Christmas is: {0}",
|
||||
arg0: afterXmas);
|
||||
|
||||
TimeSpan untilChristmas = christmas - DateTime.Now;
|
||||
|
||||
WriteLine("There are {0} days and {1} hours until Christmas.",
|
||||
arg0: untilChristmas.Days,
|
||||
arg1: untilChristmas.Hours);
|
||||
|
||||
WriteLine("There are {0:N0} hours until Christmas.",
|
||||
arg0: untilChristmas.TotalHours);
|
||||
|
||||
#!csharp
|
||||
|
||||
DateTime kidsWakeUp = new(
|
||||
year: 2021, month: 12, day: 25,
|
||||
hour: 6, minute: 30, second: 0);
|
||||
|
||||
WriteLine("Kids wake up on Christmas: {0}",
|
||||
arg0: kidsWakeUp);
|
||||
|
||||
WriteLine("The kids woke me up at {0}",
|
||||
arg0: kidsWakeUp.ToShortTimeString());
|
||||
|
||||
#!markdown
|
||||
|
||||
## Globalization with dates and times
|
||||
|
||||
The current culture controls how dates and times are parsed:
|
||||
|
||||
#!csharp
|
||||
|
||||
using System.Globalization;
|
||||
|
||||
#!csharp
|
||||
|
||||
// use the following line to switch to British culture
|
||||
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("en-GB");
|
||||
|
||||
WriteLine("Current culture is: {0}",
|
||||
arg0: CultureInfo.CurrentCulture.Name);
|
||||
|
||||
string textDate = "4 July 2021";
|
||||
DateTime independenceDay = DateTime.Parse(textDate);
|
||||
|
||||
WriteLine("Text: {0}, DateTime: {1:d MMMM}",
|
||||
arg0: textDate,
|
||||
arg1: independenceDay);
|
||||
|
||||
textDate = "7/4/2021";
|
||||
independenceDay = DateTime.Parse(textDate);
|
||||
|
||||
WriteLine("Text: {0}, DateTime: {1:d MMMM}",
|
||||
arg0: textDate,
|
||||
arg1: independenceDay);
|
||||
|
||||
independenceDay = DateTime.Parse(textDate,
|
||||
provider: CultureInfo.GetCultureInfo("en-US"));
|
||||
|
||||
WriteLine("Text: {0}, DateTime: {1:d MMMM}",
|
||||
arg0: textDate,
|
||||
arg1: independenceDay);
|
||||
|
||||
#!csharp
|
||||
|
||||
for (int year = 2020; year < 2026; year++)
|
||||
{
|
||||
Write($"{year} is a leap year: {DateTime.IsLeapYear(year)}. ");
|
||||
WriteLine("There are {0} days in February {1}.",
|
||||
arg0: DateTime.DaysInMonth(year: year, month: 2), arg1: year);
|
||||
}
|
||||
WriteLine("Is Christmas daylight saving time? {0}",
|
||||
arg0: christmas.IsDaylightSavingTime());
|
||||
|
||||
WriteLine("Is July 4th daylight saving time? {0}",
|
||||
arg0: independenceDay.IsDaylightSavingTime());
|
||||
|
||||
#!markdown
|
||||
|
||||
## Working with only a date or a time
|
||||
|
||||
.NET 6 introduces some new types for working with only a date value or only a time value named DateOnly and TimeOnly.
|
||||
|
||||
#!csharp
|
||||
|
||||
DateOnly queensBirthday = new(year: 2022, month: 4, day: 21);
|
||||
WriteLine($"The Queen's next birthday is on {queensBirthday}.");
|
||||
|
||||
TimeOnly partyStarts = new(hour: 20, minute: 30);
|
||||
WriteLine($"The Queen's party starts at {partyStarts}.");
|
||||
|
||||
DateTime calendarEntry = queensBirthday.ToDateTime(partyStarts);
|
||||
WriteLine($"Add to your calendar: {calendarEntry}.");
|
||||
|
||||
#!markdown
|
||||
|
||||
# Pattern matching with regular expressions
|
||||
|
||||
Regular expressions are useful for validating input from the user.
|
||||
|
||||
#!csharp
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
#!markdown
|
||||
|
||||
## Checking for digits entered as text
|
||||
|
||||
#!csharp
|
||||
|
||||
#nullable enable // required to support string?
|
||||
|
||||
// Write("Enter your age: ");
|
||||
string? input = "34"; // ReadLine();
|
||||
|
||||
Regex ageChecker = new(@"^\d+$");
|
||||
|
||||
if (ageChecker.IsMatch(input))
|
||||
{
|
||||
WriteLine("Thank you!");
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLine($"This is not a valid age: {input}");
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Splitting a complex comma-separated string
|
||||
|
||||
Earlier in this chapter, you learned how to split a simple comma-separated `string` variable. But what about the following example of film titles?
|
||||
```
|
||||
"Monsters, Inc.","I, Tonya","Lock, Stock and Two Smoking Barrels"
|
||||
```
|
||||
|
||||
#!csharp
|
||||
|
||||
string films = "\"Monsters, Inc.\",\"I, Tonya\",\"Lock, Stock and Two Smoking Barrels\"";
|
||||
|
||||
WriteLine($"Films to split: {films}");
|
||||
|
||||
string[] filmsDumb = films.Split(',');
|
||||
|
||||
WriteLine("Splitting with string.Split method:");
|
||||
foreach (string film in filmsDumb)
|
||||
{
|
||||
WriteLine(film);
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
WriteLine();
|
||||
|
||||
Regex csv = new(
|
||||
"(?:^|,)(?=[^\"]|(\")?)\"?((?(1)[^\"]*|[^,\"]*))\"?(?=,|$)");
|
||||
|
||||
MatchCollection filmsSmart = csv.Matches(films);
|
||||
|
||||
WriteLine("Splitting with regular expression:");
|
||||
foreach (Match film in filmsSmart)
|
||||
{
|
||||
WriteLine(film.Groups[2].Value);
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
# Storing multiple objects in collections
|
||||
|
||||
Another of the most common types of data is collections. If you need to store multiple values in a variable, then you can use a collection.
|
||||
|
||||
#!csharp
|
||||
|
||||
static void Output(string title, IEnumerable<string> collection)
|
||||
{
|
||||
WriteLine(title);
|
||||
foreach (string item in collection)
|
||||
{
|
||||
WriteLine($" {item}");
|
||||
}
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Working with lists
|
||||
|
||||
#!csharp
|
||||
|
||||
static void WorkingWithLists()
|
||||
{
|
||||
// Simple syntax for creating a list and adding three items
|
||||
List<string> cities = new();
|
||||
cities.Add("London");
|
||||
cities.Add("Paris");
|
||||
cities.Add("Milan");
|
||||
|
||||
/* Alternative syntax that is converted by the compiler into
|
||||
the three Add method calls above
|
||||
List<string> cities = new()
|
||||
{ "London", "Paris", "Milan" };
|
||||
*/
|
||||
|
||||
/* Alternative syntax that passes an
|
||||
array of string values to AddRange method
|
||||
List<string> cities = new();
|
||||
cities.AddRange(new[] { "London", "Paris", "Milan" });
|
||||
*/
|
||||
|
||||
Output("Initial list", cities);
|
||||
WriteLine($"The first city is {cities[0]}.");
|
||||
WriteLine($"The last city is {cities[cities.Count - 1]}.");
|
||||
cities.Insert(0, "Sydney");
|
||||
Output("After inserting Sydney at index 0", cities);
|
||||
cities.RemoveAt(1);
|
||||
cities.Remove("Milan");
|
||||
Output("After removing two cities", cities);
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
WorkingWithLists();
|
||||
|
||||
#!markdown
|
||||
|
||||
## Working with dictionaries
|
||||
|
||||
#!csharp
|
||||
|
||||
static void WorkingWithDictionaries()
|
||||
{
|
||||
Dictionary<string, string> keywords = new();
|
||||
|
||||
// add using named parameters
|
||||
keywords.Add(key: "int", value: "32-bit integer data type");
|
||||
|
||||
// add using positional parameters
|
||||
keywords.Add("long", "64-bit integer data type");
|
||||
keywords.Add("float", "Single precision floating point number");
|
||||
|
||||
/* Alternative syntax; compiler converts this to calls to Add method
|
||||
Dictionary<string, string> keywords = new()
|
||||
{
|
||||
{ "int", "32-bit integer data type" },
|
||||
{ "long", "64-bit integer data type" },
|
||||
{ "float", "Single precision floating point number" },
|
||||
}; */
|
||||
|
||||
/* Alternative syntax; compiler converts this to calls to Add method
|
||||
Dictionary<string, string> keywords = new()
|
||||
{
|
||||
["int"] = "32-bit integer data type",
|
||||
["long"] = "64-bit integer data type",
|
||||
["float"] = "Single precision floating point number"
|
||||
}; */
|
||||
|
||||
Output("Dictionary keys:", keywords.Keys);
|
||||
Output("Dictionary values:", keywords.Values);
|
||||
|
||||
WriteLine("Keywords and their definitions");
|
||||
foreach (KeyValuePair<string, string> item in keywords)
|
||||
{
|
||||
WriteLine($" {item.Key}: {item.Value}");
|
||||
}
|
||||
|
||||
// lookup a value using a key
|
||||
string key = "long";
|
||||
WriteLine($"The definition of {key} is {keywords[key]}");
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
WorkingWithDictionaries();
|
||||
|
||||
#!markdown
|
||||
|
||||
## Working with queues
|
||||
|
||||
#!csharp
|
||||
|
||||
static void WorkingWithQueues()
|
||||
{
|
||||
Queue<string> coffee = new();
|
||||
|
||||
coffee.Enqueue("Damir"); // front of queue
|
||||
coffee.Enqueue("Andrea");
|
||||
coffee.Enqueue("Ronald");
|
||||
coffee.Enqueue("Amin");
|
||||
coffee.Enqueue("Irina"); // back of queue
|
||||
|
||||
Output("Initial queue from front to back", coffee);
|
||||
|
||||
// server handles next person in queue
|
||||
string served = coffee.Dequeue();
|
||||
WriteLine($"Served: {served}.");
|
||||
|
||||
// server handles next person in queue
|
||||
served = coffee.Dequeue();
|
||||
WriteLine($"Served: {served}.");
|
||||
|
||||
Output("Current queue from front to back", coffee);
|
||||
|
||||
WriteLine($"{coffee.Peek()} is next in line.");
|
||||
|
||||
Output("Current queue from front to back", coffee);
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
WorkingWithQueues();
|
||||
|
||||
#!csharp
|
||||
|
||||
static void OutputPQ<TElement, TPriority>(string title,
|
||||
IEnumerable<(TElement Element, TPriority Priority)> collection)
|
||||
{
|
||||
WriteLine(title);
|
||||
foreach ((TElement, TPriority) item in collection)
|
||||
{
|
||||
WriteLine($" {item.Item1}: {item.Item2}");
|
||||
}
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
static void WorkingWithPriorityQueues()
|
||||
{
|
||||
PriorityQueue<string, int> vaccine = new();
|
||||
|
||||
// add some people
|
||||
// 1 = high priority people in their 70s or poor health
|
||||
// 2 = medium priority e.g. middle aged
|
||||
// 3 = low priority e.g. teens and twenties
|
||||
vaccine.Enqueue("Pamela", 1); // my mum (70s)
|
||||
vaccine.Enqueue("Rebecca", 3); // my niece (teens)
|
||||
vaccine.Enqueue("Juliet", 2); // my sister (40s)
|
||||
vaccine.Enqueue("Ian", 1); // my dad (70s)
|
||||
|
||||
OutputPQ("Current queue for vaccination:", vaccine.UnorderedItems);
|
||||
|
||||
WriteLine($"{vaccine.Dequeue()} has been vaccinated.");
|
||||
WriteLine($"{vaccine.Dequeue()} has been vaccinated.");
|
||||
|
||||
OutputPQ("Current queue for vaccination:", vaccine.UnorderedItems);
|
||||
|
||||
WriteLine($"{vaccine.Dequeue()} has been vaccinated.");
|
||||
vaccine.Enqueue("Mark", 2); // me (40s)
|
||||
WriteLine($"{vaccine.Peek()} will be next to be vaccinated.");
|
||||
OutputPQ("Current queue for vaccination:", vaccine.UnorderedItems);
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
WorkingWithPriorityQueues();
|
||||
|
||||
#!markdown
|
||||
|
||||
## Using immutable collections
|
||||
|
||||
#!csharp
|
||||
|
||||
using System.Collections.Immutable;
|
||||
|
||||
#!csharp
|
||||
|
||||
static void WorkingWithLists2()
|
||||
{
|
||||
// Simple syntax for creating a list and adding three items
|
||||
List<string> cities = new();
|
||||
cities.Add("London");
|
||||
cities.Add("Paris");
|
||||
cities.Add("Milan");
|
||||
|
||||
/* Alternative syntax that is converted by the compiler into
|
||||
the three Add method calls above
|
||||
List<string> cities = new()
|
||||
{ "London", "Paris", "Milan" };
|
||||
*/
|
||||
|
||||
/* Alternative syntax that passes an
|
||||
array of string values to AddRange method
|
||||
List<string> cities = new();
|
||||
cities.AddRange(new[] { "London", "Paris", "Milan" });
|
||||
*/
|
||||
|
||||
Output("Initial list", cities);
|
||||
WriteLine($"The first city is {cities[0]}.");
|
||||
WriteLine($"The last city is {cities[cities.Count - 1]}.");
|
||||
cities.Insert(0, "Sydney");
|
||||
Output("After inserting Sydney at index 0", cities);
|
||||
cities.RemoveAt(1);
|
||||
cities.Remove("Milan");
|
||||
Output("After removing two cities", cities);
|
||||
|
||||
ImmutableList<string> immutableCities = cities.ToImmutableList();
|
||||
ImmutableList<string> newList = immutableCities.Add("Rio");
|
||||
|
||||
Output("Immutable list of cities:", immutableCities);
|
||||
Output("New list of cities:", newList);
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
WorkingWithLists2();
|
||||
|
||||
#!markdown
|
||||
|
||||
# Working with spans, indexes, and ranges
|
||||
|
||||
C# 8.0 introduced two features for identifying an item's index within an array and a range of items using two indexes.
|
||||
|
||||
#!markdown
|
||||
|
||||
## Identifying ranges with the Range type
|
||||
|
||||
#!csharp
|
||||
|
||||
Range r1 = new(start: new Index(3), end: new Index(7));
|
||||
Range r2 = new(start: 3, end: 7); // using implicit int conversion
|
||||
Range r3 = 3..7; // using C# 8.0 or later syntax
|
||||
Range r4 = Range.StartAt(3); // from index 3 to last index
|
||||
Range r5 = 3..; // from index 3 to last index
|
||||
Range r6 = Range.EndAt(3); // from index 0 to index 3
|
||||
Range r7 = ..3; // from index 0 to index 3
|
||||
|
||||
#!markdown
|
||||
|
||||
## Using indexes, ranges, and spans
|
||||
|
||||
#!csharp
|
||||
|
||||
string name = "Samantha Jones";
|
||||
|
||||
// Using Substring
|
||||
|
||||
int lengthOfFirst = name.IndexOf(' ');
|
||||
int lengthOfLast = name.Length - lengthOfFirst - 1;
|
||||
|
||||
string firstName = name.Substring(
|
||||
startIndex: 0,
|
||||
length: lengthOfFirst);
|
||||
|
||||
string lastName = name.Substring(
|
||||
startIndex: name.Length - lengthOfLast,
|
||||
length: lengthOfLast);
|
||||
|
||||
WriteLine($"First name: {firstName}, Last name: {lastName}");
|
||||
|
||||
// Using spans
|
||||
|
||||
ReadOnlySpan<char> nameAsSpan = name.AsSpan();
|
||||
ReadOnlySpan<char> firstNameSpan = nameAsSpan[0..lengthOfFirst];
|
||||
ReadOnlySpan<char> lastNameSpan = nameAsSpan[^lengthOfLast..^0];
|
||||
|
||||
WriteLine("First name: {0}, Last name: {1}",
|
||||
arg0: firstNameSpan.ToString(),
|
||||
arg1: lastNameSpan.ToString());
|
||||
|
||||
#!markdown
|
||||
|
||||
# Working with network resources
|
||||
|
||||
Sometimes you will need to work with network resources.
|
||||
|
||||
#!markdown
|
||||
|
||||
## Working with URIs, DNS, and IP addresses
|
||||
|
||||
#!csharp
|
||||
|
||||
using System.Net; // IPHostEntry, Dns, IPAddress
|
||||
|
||||
#!csharp
|
||||
|
||||
#nullable enable
|
||||
|
||||
// Write("Enter a valid web address: ");
|
||||
string? url = "http://google.com"; // ReadLine();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(url))
|
||||
{
|
||||
url = "https://stackoverflow.com/search?q=securestring";
|
||||
}
|
||||
|
||||
Uri uri = new(url);
|
||||
|
||||
WriteLine($"URL: {url}");
|
||||
WriteLine($"Scheme: {uri.Scheme}");
|
||||
WriteLine($"Port: {uri.Port}");
|
||||
WriteLine($"Host: {uri.Host}");
|
||||
WriteLine($"Path: {uri.AbsolutePath}");
|
||||
WriteLine($"Query: {uri.Query}");
|
||||
|
||||
#!csharp
|
||||
|
||||
IPHostEntry entry = Dns.GetHostEntry(uri.Host);
|
||||
WriteLine($"{entry.HostName} has the following IP addresses:");
|
||||
foreach (IPAddress address in entry.AddressList)
|
||||
{
|
||||
WriteLine($" {address} ({address.AddressFamily})");
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Pinging a server
|
||||
|
||||
#!csharp
|
||||
|
||||
using System.Net.NetworkInformation; // Ping, PingReply, IPStatus
|
||||
|
||||
#!csharp
|
||||
|
||||
try
|
||||
{
|
||||
Ping ping = new();
|
||||
WriteLine("Pinging server. Please wait...");
|
||||
PingReply reply = ping.Send(uri.Host);
|
||||
|
||||
WriteLine($"{uri.Host} was pinged and replied: {reply.Status}.");
|
||||
if (reply.Status == IPStatus.Success)
|
||||
{
|
||||
WriteLine("Reply from {0} took {1:N0}ms",
|
||||
arg0: reply.Address,
|
||||
arg1: reply.RoundtripTime);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteLine($"{ex.GetType().ToString()} says {ex.Message}");
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
# Working with reflection and attributes
|
||||
|
||||
Reflection is a programming feature that allows code to understand and manipulate itself.
|
||||
|
||||
#!csharp
|
||||
|
||||
using System.Reflection; // Assembly
|
||||
|
||||
#!markdown
|
||||
|
||||
## Reading assembly metadata
|
||||
|
||||
Note: This example works best in a console application. In a .NET Interactive notebook, the assembly is `Microsoft.DotNet.Interactive.App`.
|
||||
|
||||
#!csharp
|
||||
|
||||
#nullable enable
|
||||
|
||||
WriteLine("Assembly metadata:");
|
||||
Assembly? assembly = Assembly.GetEntryAssembly();
|
||||
if (assembly is null)
|
||||
{
|
||||
WriteLine("Failed to get entry assembly.");
|
||||
return;
|
||||
}
|
||||
|
||||
WriteLine($" Full name: {assembly.FullName}");
|
||||
WriteLine($" Location: {assembly.Location}");
|
||||
|
||||
IEnumerable<Attribute> attributes = assembly.GetCustomAttributes();
|
||||
|
||||
WriteLine($" Assembly-level attributes:");
|
||||
foreach (Attribute a in attributes)
|
||||
{
|
||||
WriteLine($" {a.GetType()}");
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
#nullable enable
|
||||
|
||||
AssemblyInformationalVersionAttribute? version = assembly
|
||||
.GetCustomAttribute<AssemblyInformationalVersionAttribute>();
|
||||
|
||||
WriteLine($" Version: {version?.InformationalVersion}");
|
||||
|
||||
AssemblyCompanyAttribute? company = assembly
|
||||
.GetCustomAttribute<AssemblyCompanyAttribute>();
|
||||
|
||||
WriteLine($" Company: {company?.Company}");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Creating custom attributes
|
||||
|
||||
This section should only be tried in a console application.
|
||||
|
||||
#!markdown
|
||||
|
||||
# Working with images
|
||||
|
||||
ImageSharp is a third-party cross-platform 2D graphics library.
|
||||
|
||||
#!csharp
|
||||
|
||||
#r "nuget:SixLabors.ImageSharp,1.0.3"
|
||||
|
||||
#!csharp
|
||||
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
using System.IO;
|
||||
|
||||
#!csharp
|
||||
|
||||
string imagesFolder = Path.Combine(
|
||||
Environment.CurrentDirectory, "images");
|
||||
|
||||
IEnumerable<string> images =
|
||||
Directory.EnumerateFiles(imagesFolder);
|
||||
|
||||
foreach (string imagePath in images)
|
||||
{
|
||||
string thumbnailPath = Path.Combine(
|
||||
Environment.CurrentDirectory, "images",
|
||||
Path.GetFileNameWithoutExtension(imagePath)
|
||||
+ "-thumbnail" + Path.GetExtension(imagePath));
|
||||
|
||||
using (Image image = Image.Load(imagePath))
|
||||
{
|
||||
image.Mutate(x => x.Resize(image.Width / 10, image.Height / 10));
|
||||
image.Mutate(x => x.Grayscale());
|
||||
image.Save(thumbnailPath);
|
||||
}
|
||||
}
|
||||
WriteLine("Image processing complete. View the images folder.");
|
||||
|
||||
#!markdown
|
||||
|
||||
# Internationalizing your code
|
||||
|
||||
#!csharp
|
||||
|
||||
using System.Globalization; // CultureInfo
|
||||
|
||||
#!csharp
|
||||
|
||||
#nullable enable
|
||||
|
||||
CultureInfo globalization = CultureInfo.CurrentCulture;
|
||||
CultureInfo localization = CultureInfo.CurrentUICulture;
|
||||
|
||||
WriteLine("The current globalization culture is {0}: {1}",
|
||||
globalization.Name, globalization.DisplayName);
|
||||
|
||||
WriteLine("The current localization culture is {0}: {1}",
|
||||
localization.Name, localization.DisplayName);
|
||||
|
||||
WriteLine();
|
||||
|
||||
WriteLine("en-US: English (United States)");
|
||||
WriteLine("da-DK: Danish (Denmark)");
|
||||
WriteLine("fr-CA: French (Canada)");
|
||||
|
||||
// Write("Enter an ISO culture code: ");
|
||||
string? newCulture = "da-DK"; // ReadLine();
|
||||
|
||||
if (!string.IsNullOrEmpty(newCulture))
|
||||
{
|
||||
CultureInfo ci = new(newCulture);
|
||||
|
||||
// change the current cultures
|
||||
CultureInfo.CurrentCulture = ci;
|
||||
CultureInfo.CurrentUICulture = ci;
|
||||
}
|
||||
WriteLine();
|
||||
|
||||
// Write("Enter your name: ");
|
||||
string? name = "Mikkel"; // ReadLine();
|
||||
|
||||
// Write("Enter your date of birth: ");
|
||||
string? dob = "12/3/1980"; // ReadLine();
|
||||
|
||||
// Write("Enter your salary: ");
|
||||
string? salary = "340000"; // ReadLine();
|
||||
|
||||
DateTime date = DateTime.Parse(dob);
|
||||
int minutes = (int)DateTime.Today.Subtract(date).TotalMinutes;
|
||||
decimal earns = decimal.Parse(salary);
|
||||
|
||||
WriteLine(
|
||||
"{0} was born on a {1:dddd}, is {2:N0} minutes old, and earns {3:C}",
|
||||
name, date, minutes, earns);
|
||||
675
notebooks/Chapter09.dib
Normal file
|
|
@ -0,0 +1,675 @@
|
|||
#!markdown
|
||||
|
||||
# Chapter 9 - Working with Files, Streams, and Serialization
|
||||
- Managing the filesystem
|
||||
- Reading and writing with streams
|
||||
- Encoding and decoding text
|
||||
- Serializing object graphs
|
||||
- Controlling JSON processing
|
||||
|
||||
#!markdown
|
||||
|
||||
# Managing the filesystem
|
||||
|
||||
Your applications will often need to perform input and output operations with files and
|
||||
directories in different environments. The `System` and `System.IO` namespaces contain classes
|
||||
for this purpose.
|
||||
|
||||
#!markdown
|
||||
|
||||
## Handling cross-platform environments and filesystems
|
||||
|
||||
#!csharp
|
||||
|
||||
using static System.Console;
|
||||
using static System.IO.Directory;
|
||||
using static System.IO.Path;
|
||||
using static System.Environment;
|
||||
|
||||
#!csharp
|
||||
|
||||
using System.IO;
|
||||
|
||||
#!csharp
|
||||
|
||||
WriteLine("{0,-33} {1}", arg0: "Path.PathSeparator",
|
||||
arg1: PathSeparator);
|
||||
WriteLine("{0,-33} {1}", arg0: "Path.DirectorySeparatorChar",
|
||||
arg1: DirectorySeparatorChar);
|
||||
WriteLine("{0,-33} {1}", arg0: "Directory.GetCurrentDirectory()",
|
||||
arg1: GetCurrentDirectory());
|
||||
WriteLine("{0,-33} {1}", arg0: "Environment.CurrentDirectory",
|
||||
arg1: CurrentDirectory);
|
||||
WriteLine("{0,-33} {1}", arg0: "Environment.SystemDirectory",
|
||||
arg1: SystemDirectory);
|
||||
WriteLine("{0,-33} {1}", arg0: "Path.GetTempPath()",
|
||||
arg1: GetTempPath());
|
||||
|
||||
WriteLine("GetFolderPath(SpecialFolder");
|
||||
WriteLine("{0,-33} {1}", arg0: " .System)",
|
||||
arg1: GetFolderPath(SpecialFolder.System));
|
||||
WriteLine("{0,-33} {1}", arg0: " .ApplicationData)",
|
||||
arg1: GetFolderPath(SpecialFolder.ApplicationData));
|
||||
WriteLine("{0,-33} {1}", arg0: " .MyDocuments)",
|
||||
arg1: GetFolderPath(SpecialFolder.MyDocuments));
|
||||
WriteLine("{0,-33} {1}", arg0: " .Personal)",
|
||||
arg1: GetFolderPath(SpecialFolder.Personal));
|
||||
|
||||
#!markdown
|
||||
|
||||
## Managing drives
|
||||
|
||||
#!csharp
|
||||
|
||||
WriteLine("{0,-30} | {1,-10} | {2,-7} | {3,18} | {4,18}",
|
||||
"NAME", "TYPE", "FORMAT", "SIZE (BYTES)", "FREE SPACE");
|
||||
|
||||
foreach (DriveInfo drive in DriveInfo.GetDrives())
|
||||
{
|
||||
if (drive.IsReady)
|
||||
{
|
||||
WriteLine(
|
||||
"{0,-30} | {1,-10} | {2,-7} | {3,18:N0} | {4,18:N0}",
|
||||
drive.Name, drive.DriveType, drive.DriveFormat,
|
||||
drive.TotalSize, drive.AvailableFreeSpace);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLine("{0,-30} | {1,-10}", drive.Name, drive.DriveType);
|
||||
}
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Managing directories
|
||||
|
||||
#!csharp
|
||||
|
||||
// define a directory path for a new folder
|
||||
// starting in the user's folder
|
||||
string newFolder = Combine(
|
||||
GetFolderPath(SpecialFolder.Personal),
|
||||
"Code", "Chapter09", "NewFolder");
|
||||
|
||||
WriteLine($"Working with: {newFolder}");
|
||||
|
||||
// check if it exists
|
||||
WriteLine($"Does it exist? {Exists(newFolder)}");
|
||||
|
||||
// create directory
|
||||
WriteLine("Creating it...");
|
||||
CreateDirectory(newFolder);
|
||||
WriteLine($"Does it exist? {Exists(newFolder)}");
|
||||
Write("Confirm the directory exists, and then execute the next notebook cell.");
|
||||
|
||||
#!csharp
|
||||
|
||||
// delete directory
|
||||
WriteLine("Deleting it...");
|
||||
Delete(newFolder, recursive: true);
|
||||
WriteLine($"Does it exist? {Exists(newFolder)}");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Managing files
|
||||
|
||||
#!csharp
|
||||
|
||||
// define a directory path to output files
|
||||
// starting in the user's folder
|
||||
string dir = Combine(
|
||||
GetFolderPath(SpecialFolder.Personal),
|
||||
"Code", "Chapter09", "OutputFiles");
|
||||
|
||||
CreateDirectory(dir);
|
||||
|
||||
// define file paths
|
||||
string textFile = Combine(dir, "Dummy.txt");
|
||||
string backupFile = Combine(dir, "Dummy.bak");
|
||||
WriteLine($"Working with: {textFile}");
|
||||
|
||||
// check if a file exists
|
||||
WriteLine($"Does it exist? {File.Exists(textFile)}");
|
||||
|
||||
// create a new text file and write a line to it
|
||||
StreamWriter textWriter = File.CreateText(textFile);
|
||||
textWriter.WriteLine("Hello, C#!");
|
||||
textWriter.Close(); // close file and release resources
|
||||
WriteLine($"Does it exist? {File.Exists(textFile)}");
|
||||
|
||||
// copy the file, and overwrite if it already exists
|
||||
File.Copy(sourceFileName: textFile,
|
||||
destFileName: backupFile, overwrite: true);
|
||||
WriteLine(
|
||||
$"Does {backupFile} exist? {File.Exists(backupFile)}");
|
||||
Write("Confirm the files exist, and then run the next notebook cell.");
|
||||
|
||||
#!csharp
|
||||
|
||||
// delete file
|
||||
File.Delete(textFile);
|
||||
WriteLine($"Does it exist? {File.Exists(textFile)}");
|
||||
|
||||
// read from the text file backup
|
||||
WriteLine($"Reading contents of {backupFile}:");
|
||||
StreamReader textReader = File.OpenText(backupFile);
|
||||
WriteLine(textReader.ReadToEnd());
|
||||
textReader.Close();
|
||||
|
||||
#!markdown
|
||||
|
||||
## Managing paths
|
||||
|
||||
#!csharp
|
||||
|
||||
// Managing paths
|
||||
WriteLine($"Folder Name: {GetDirectoryName(textFile)}");
|
||||
WriteLine($"File Name: {GetFileName(textFile)}");
|
||||
WriteLine("File Name without Extension: {0}",
|
||||
GetFileNameWithoutExtension(textFile));
|
||||
WriteLine($"File Extension: {GetExtension(textFile)}");
|
||||
WriteLine($"Random File Name: {GetRandomFileName()}");
|
||||
WriteLine($"Temporary File Name: {GetTempFileName()}");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Getting file information
|
||||
|
||||
#!csharp
|
||||
|
||||
FileInfo info = new(backupFile);
|
||||
WriteLine($"{backupFile}:");
|
||||
WriteLine($"Contains {info.Length} bytes");
|
||||
WriteLine($"Last accessed {info.LastAccessTime}");
|
||||
WriteLine($"Has readonly set to {info.IsReadOnly}");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Controlling how you work with files
|
||||
|
||||
#!csharp
|
||||
|
||||
FileInfo info = new(backupFile);
|
||||
WriteLine("Is the backup file compressed? {0}",
|
||||
info.Attributes.HasFlag(FileAttributes.Compressed));
|
||||
|
||||
#!markdown
|
||||
|
||||
# Reading and writing with streams
|
||||
|
||||
#!markdown
|
||||
|
||||
## Writing to text streams
|
||||
|
||||
#!csharp
|
||||
|
||||
static class Viper
|
||||
{
|
||||
// define an array of Viper pilot call signs
|
||||
public static string[] Callsigns = new[]
|
||||
{
|
||||
"Husker", "Starbuck", "Apollo", "Boomer",
|
||||
"Bulldog", "Athena", "Helo", "Racetrack"
|
||||
};
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
// define a file to write to
|
||||
string textFile = Combine(CurrentDirectory, "streams.txt");
|
||||
|
||||
// create a text file and return a helper writer
|
||||
StreamWriter text = File.CreateText(textFile);
|
||||
|
||||
// enumerate the strings, writing each one
|
||||
// to the stream on a separate line
|
||||
foreach (string item in Viper.Callsigns)
|
||||
{
|
||||
text.WriteLine(item);
|
||||
}
|
||||
text.Close(); // release resources
|
||||
|
||||
// output the contents of the file
|
||||
WriteLine("{0} contains {1:N0} bytes.",
|
||||
arg0: textFile,
|
||||
arg1: new FileInfo(textFile).Length);
|
||||
|
||||
WriteLine(File.ReadAllText(textFile));
|
||||
|
||||
#!markdown
|
||||
|
||||
## Writing to XML streams
|
||||
|
||||
#!csharp
|
||||
|
||||
using System.Xml;
|
||||
|
||||
#!csharp
|
||||
|
||||
#nullable enable
|
||||
|
||||
FileStream? xmlFileStream = null;
|
||||
XmlWriter? xml = null;
|
||||
|
||||
try
|
||||
{
|
||||
// define a file to write to
|
||||
string xmlFile = Combine(CurrentDirectory, "streams.xml");
|
||||
|
||||
// create a file stream
|
||||
xmlFileStream = File.Create(xmlFile);
|
||||
|
||||
// wrap the file stream in an XML writer helper
|
||||
// and automatically indent nested elements
|
||||
xml = XmlWriter.Create(xmlFileStream,
|
||||
new XmlWriterSettings { Indent = true });
|
||||
|
||||
// write the XML declaration
|
||||
xml.WriteStartDocument();
|
||||
// write a root element
|
||||
xml.WriteStartElement("callsigns");
|
||||
|
||||
// enumerate the strings writing each one to the stream
|
||||
foreach (string item in Viper.Callsigns)
|
||||
{
|
||||
xml.WriteElementString("callsign", item);
|
||||
}
|
||||
|
||||
// write the close root element
|
||||
xml.WriteEndElement();
|
||||
|
||||
// close helper and stream
|
||||
xml.Close();
|
||||
xmlFileStream.Close();
|
||||
|
||||
// output all the contents of the file
|
||||
WriteLine("{0} contains {1:N0} bytes.",
|
||||
arg0: xmlFile,
|
||||
arg1: new FileInfo(xmlFile).Length);
|
||||
|
||||
WriteLine(File.ReadAllText(xmlFile));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// if the path doesn't exist the exception will be caught
|
||||
WriteLine($"{ex.GetType()} says {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (xml != null)
|
||||
{
|
||||
xml.Dispose();
|
||||
WriteLine("The XML writer's unmanaged resources have been disposed.");
|
||||
|
||||
if (xmlFileStream != null)
|
||||
{
|
||||
xmlFileStream.Dispose();
|
||||
WriteLine("The file stream's unmanaged resources have been disposed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Simplifying disposal by using the using statement
|
||||
|
||||
#!csharp
|
||||
|
||||
using FileStream file2 = File.OpenWrite(
|
||||
Path.Combine(CurrentDirectory, "file2.txt"));
|
||||
|
||||
using StreamWriter writer2 = new(file2);
|
||||
|
||||
try
|
||||
{
|
||||
writer2.WriteLine("Welcome, .NET!");
|
||||
}
|
||||
catch(Exception ex)
|
||||
{
|
||||
WriteLine($"{ex.GetType()} says {ex.Message}");
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Compressing streams
|
||||
|
||||
#!csharp
|
||||
|
||||
using System.IO.Compression; // BrotliStream, GZipStream, CompressionMode
|
||||
|
||||
#!csharp
|
||||
|
||||
static void WorkWithCompression(bool useBrotli = true)
|
||||
{
|
||||
string fileExt = useBrotli ? "brotli" : "gzip";
|
||||
|
||||
// compress the XML output
|
||||
string filePath = Combine(
|
||||
CurrentDirectory, $"streams.{fileExt}");
|
||||
|
||||
FileStream file = File.Create(filePath);
|
||||
|
||||
Stream compressor;
|
||||
|
||||
if (useBrotli)
|
||||
{
|
||||
compressor = new BrotliStream(file, CompressionMode.Compress);
|
||||
}
|
||||
else
|
||||
{
|
||||
compressor = new GZipStream(file, CompressionMode.Compress);
|
||||
}
|
||||
|
||||
using (compressor)
|
||||
{
|
||||
using (XmlWriter xml = XmlWriter.Create(compressor))
|
||||
{
|
||||
xml.WriteStartDocument();
|
||||
xml.WriteStartElement("callsigns");
|
||||
foreach (string item in Viper.Callsigns)
|
||||
{
|
||||
xml.WriteElementString("callsign", item);
|
||||
}
|
||||
}
|
||||
} // also closes the underlying stream
|
||||
|
||||
// output all the contents of the compressed file
|
||||
WriteLine("{0} contains {1:N0} bytes.",
|
||||
filePath, new FileInfo(filePath).Length);
|
||||
|
||||
WriteLine($"The compressed contents:");
|
||||
WriteLine(File.ReadAllText(filePath));
|
||||
|
||||
// read a compressed file
|
||||
WriteLine("Reading the compressed XML file:");
|
||||
file = File.Open(filePath, FileMode.Open);
|
||||
|
||||
Stream decompressor;
|
||||
if (useBrotli)
|
||||
{
|
||||
decompressor = new BrotliStream(
|
||||
file, CompressionMode.Decompress);
|
||||
}
|
||||
else
|
||||
{
|
||||
decompressor = new GZipStream(
|
||||
file, CompressionMode.Decompress);
|
||||
}
|
||||
|
||||
using (decompressor)
|
||||
{
|
||||
using (XmlReader reader = XmlReader.Create(decompressor))
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
// check if we are on an element node named callsign
|
||||
if ((reader.NodeType == XmlNodeType.Element)
|
||||
&& (reader.Name == "callsign"))
|
||||
{
|
||||
reader.Read(); // move to the text inside element
|
||||
WriteLine($"{reader.Value}"); // read its value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
WorkWithCompression();
|
||||
WorkWithCompression(useBrotli: false);
|
||||
|
||||
#!markdown
|
||||
|
||||
# Encoding and decoding text
|
||||
|
||||
Change the `ConsoleKey` value to D1 to D5 to select the encoding that you want to use.
|
||||
|
||||
#!csharp
|
||||
|
||||
// choose an encoding
|
||||
Write("Press a number to choose an encoding: ");
|
||||
ConsoleKey number = ConsoleKey.D3; // ReadKey(intercept: false).Key;
|
||||
WriteLine();
|
||||
WriteLine();
|
||||
|
||||
Encoding encoder = number switch
|
||||
{
|
||||
ConsoleKey.D1 => Encoding.ASCII,
|
||||
ConsoleKey.D2 => Encoding.UTF7,
|
||||
ConsoleKey.D3 => Encoding.UTF8,
|
||||
ConsoleKey.D4 => Encoding.Unicode,
|
||||
ConsoleKey.D5 => Encoding.UTF32,
|
||||
_ => Encoding.Default
|
||||
};
|
||||
|
||||
// define a string to encode
|
||||
string message = "Café cost: £4.39";
|
||||
|
||||
// encode the string into a byte array
|
||||
byte[] encoded = encoder.GetBytes(message);
|
||||
|
||||
// check how many bytes the encoding needed
|
||||
WriteLine("{0} uses {1:N0} bytes.",
|
||||
encoder.GetType().Name, encoded.Length);
|
||||
WriteLine();
|
||||
|
||||
// enumerate each byte
|
||||
WriteLine($"BYTE HEX CHAR");
|
||||
foreach (byte b in encoded)
|
||||
{
|
||||
WriteLine($"{b,4} {b.ToString("X"),4} {(char)b,5}");
|
||||
}
|
||||
// decode the byte array back into a string and display it
|
||||
string decoded = encoder.GetString(encoded);
|
||||
WriteLine(decoded);
|
||||
|
||||
#!markdown
|
||||
|
||||
# Serializing object graphs
|
||||
|
||||
Serialization is the process of converting a live object into a sequence of bytes using a specified
|
||||
format. Deserialization is the reverse process.
|
||||
|
||||
#!csharp
|
||||
|
||||
#nullable enable
|
||||
|
||||
public class Person
|
||||
{
|
||||
public Person() { } // required to use XmlSerializer
|
||||
|
||||
public Person(decimal initialSalary)
|
||||
{
|
||||
Salary = initialSalary;
|
||||
}
|
||||
|
||||
public string? FirstName { get; set; }
|
||||
public string? LastName { get; set; }
|
||||
public DateTime DateOfBirth { get; set; }
|
||||
public HashSet<Person>? Children { get; set; }
|
||||
protected decimal Salary { get; set; }
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
using System.Xml.Serialization; // XmlSerializer
|
||||
|
||||
#!csharp
|
||||
|
||||
// create an object graph
|
||||
List<Person> people = new()
|
||||
{
|
||||
new(30000M)
|
||||
{
|
||||
FirstName = "Alice",
|
||||
LastName = "Smith",
|
||||
DateOfBirth = new(1974, 3, 14)
|
||||
},
|
||||
new(40000M)
|
||||
{
|
||||
FirstName = "Bob",
|
||||
LastName = "Jones",
|
||||
DateOfBirth = new(1969, 11, 23)
|
||||
},
|
||||
new(20000M)
|
||||
{
|
||||
FirstName = "Charlie",
|
||||
LastName = "Cox",
|
||||
DateOfBirth = new(1984, 5, 4),
|
||||
Children = new()
|
||||
{
|
||||
new(0M)
|
||||
{
|
||||
FirstName = "Sally",
|
||||
LastName = "Cox",
|
||||
DateOfBirth = new(2000, 7, 12)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#!csharp
|
||||
|
||||
// create object that will format a List of Persons as XML
|
||||
XmlSerializer xs = new(people.GetType());
|
||||
WriteLine(people.GetType());
|
||||
|
||||
// create a file to write to
|
||||
string path = Combine(CurrentDirectory, "people.xml");
|
||||
WriteLine(path);
|
||||
|
||||
using (FileStream stream = File.Create(path))
|
||||
{
|
||||
// serialize the object graph to the stream
|
||||
xs.Serialize(stream, people);
|
||||
}
|
||||
|
||||
WriteLine("Written {0:N0} bytes of XML to {1}",
|
||||
arg0: new FileInfo(path).Length,
|
||||
arg1: path);
|
||||
WriteLine();
|
||||
|
||||
// Display the serialized object graph
|
||||
WriteLine(File.ReadAllText(path));
|
||||
|
||||
#!markdown
|
||||
|
||||
## Serializing with JSON
|
||||
|
||||
#!csharp
|
||||
|
||||
#r "nuget:Newtonsoft.Json,13.0.1"
|
||||
|
||||
#!csharp
|
||||
|
||||
// create a file to write to
|
||||
string jsonPath = Combine(CurrentDirectory, "people.json");
|
||||
|
||||
using (StreamWriter jsonStream = File.CreateText(jsonPath))
|
||||
{
|
||||
// create an object that will format as JSON
|
||||
Newtonsoft.Json.JsonSerializer jss = new();
|
||||
// serialize the object graph into a string
|
||||
jss.Serialize(jsonStream, people);
|
||||
}
|
||||
WriteLine();
|
||||
WriteLine("Written {0:N0} bytes of JSON to: {1}",
|
||||
arg0: new FileInfo(jsonPath).Length,
|
||||
arg1: jsonPath);
|
||||
|
||||
// Display the serialized object graph
|
||||
WriteLine(File.ReadAllText(jsonPath));
|
||||
|
||||
#!csharp
|
||||
|
||||
using NewJson = System.Text.Json.JsonSerializer;
|
||||
|
||||
#!csharp
|
||||
|
||||
using (FileStream jsonLoad = File.Open(jsonPath, FileMode.Open))
|
||||
{
|
||||
// deserialize object graph into a List of Person
|
||||
List<Person>? loadedPeople =
|
||||
await NewJson.DeserializeAsync(utf8Json: jsonLoad,
|
||||
returnType: typeof(List<Person>)) as List<Person>;
|
||||
|
||||
if (loadedPeople is not null)
|
||||
{
|
||||
foreach (Person p in loadedPeople)
|
||||
{
|
||||
WriteLine("{0} has {1} children.",
|
||||
p.LastName, p.Children?.Count ?? 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Controlling JSON processing
|
||||
|
||||
#!csharp
|
||||
|
||||
using System.Text.Json; // JsonSerializer
|
||||
using System.Text.Json.Serialization; // [JsonInclude]
|
||||
|
||||
#!csharp
|
||||
|
||||
#nullable enable
|
||||
|
||||
public class Book
|
||||
{
|
||||
// constructor to set non-nullable property
|
||||
public Book(string title)
|
||||
{
|
||||
Title = title;
|
||||
}
|
||||
|
||||
// properties
|
||||
public string Title { get; set; }
|
||||
public string? Author { get; set; }
|
||||
|
||||
// fields
|
||||
[JsonInclude] // include this field
|
||||
public DateOnly PublishDate;
|
||||
|
||||
[JsonInclude] // include this field
|
||||
public DateTimeOffset Created;
|
||||
|
||||
public ushort Pages;
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
Book csharp10 = new(title:
|
||||
"C# 10 and .NET 6 - Modern Cross-platform Development")
|
||||
{
|
||||
Author = "Mark J Price",
|
||||
PublishDate = new(year: 2021, month: 11, day: 9),
|
||||
Pages = 823,
|
||||
Created = DateTimeOffset.UtcNow,
|
||||
};
|
||||
|
||||
JsonSerializerOptions options = new()
|
||||
{
|
||||
IncludeFields = true, // includes all fields
|
||||
PropertyNameCaseInsensitive = true,
|
||||
WriteIndented = true,
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
};
|
||||
|
||||
string filePath = Combine(CurrentDirectory, "book.json");
|
||||
|
||||
using (Stream fileStream = File.Create(filePath))
|
||||
{
|
||||
JsonSerializer.Serialize<Book>(
|
||||
utf8Json: fileStream, value: csharp10, options);
|
||||
}
|
||||
|
||||
WriteLine("Written {0:N0} bytes of JSON to {1}",
|
||||
arg0: new FileInfo(filePath).Length,
|
||||
arg1: filePath);
|
||||
WriteLine();
|
||||
|
||||
// Display the serialized object graph
|
||||
WriteLine(File.ReadAllText(filePath));
|
||||
159
notebooks/Chapter10.dib
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
#!markdown
|
||||
|
||||
# Chapter 10 - Working with Data Using Entity Framework Core
|
||||
|
||||
- Understanding modern databases
|
||||
- Setting up EF Core
|
||||
- Defining EF Core models
|
||||
- Querying EF Core models
|
||||
- Loading patterns with EF Core
|
||||
- Manipulating data with EF Core
|
||||
- Working with transactions
|
||||
- Code First EF Core models
|
||||
|
||||
#!markdown
|
||||
|
||||
# Setting up EF Core
|
||||
|
||||
[.NET Interactive with SQL](https://devblogs.microsoft.com/dotnet/net-interactive-with-sql-net-notebooks-in-visual-studio-code/)
|
||||
|
||||
#!csharp
|
||||
|
||||
#r "nuget:Microsoft.DotNet.Interactive.SqlServer,1.0.0-beta.21553.5"
|
||||
|
||||
#!sql
|
||||
|
||||
#!connect mssql -h
|
||||
|
||||
#!sql
|
||||
|
||||
#!connect mssql --kernel-name Northwind "Data Source=.;Initial Catalog=Northwind;Integrated Security=true;MultipleActiveResultSets=true;"
|
||||
|
||||
#!sql
|
||||
|
||||
#!sql-Northwind
|
||||
SELECT * FROM Categories
|
||||
|
||||
#!sql
|
||||
|
||||
#!connect mssql --create-dbcontext --kernel-name Northwind "Data Source=.;Initial Catalog=Northwind;Integrated Security=true;MultipleActiveResultSets=true;"
|
||||
|
||||
#!markdown
|
||||
|
||||
## Choosing an EF Core database provider
|
||||
|
||||
Add package references to the EF Core data provider for both SQL Server and SQLite.
|
||||
|
||||
#!csharp
|
||||
|
||||
#r "nuget:Microsoft.EntityFrameworkCore.Sqlite,5.0.11"
|
||||
#r "nuget:Microsoft.EntityFrameworkCore.SqlServer,5.0.11"
|
||||
|
||||
#!markdown
|
||||
|
||||
Define a constant to switch between providers.
|
||||
|
||||
#!csharp
|
||||
|
||||
public class ProjectConstants
|
||||
{
|
||||
public const string DatabaseProvider = "SQLite"; // or "SQLServer"
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
using static System.Console;
|
||||
|
||||
#!csharp
|
||||
|
||||
WriteLine($"Using {ProjectConstants.DatabaseProvider} database provider.");
|
||||
|
||||
#!markdown
|
||||
|
||||
## Defining the Northwind database context class
|
||||
|
||||
#!csharp
|
||||
|
||||
using System.IO;
|
||||
using Microsoft.EntityFrameworkCore; // DbContext, DbContextOptionsBuilder
|
||||
|
||||
#!csharp
|
||||
|
||||
public class Northwind : DbContext
|
||||
{
|
||||
protected override void OnConfiguring(
|
||||
DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
if (ProjectConstants.DatabaseProvider == "SQLite")
|
||||
{
|
||||
string path = Path.Combine(
|
||||
Environment.CurrentDirectory, "Northwind.db");
|
||||
|
||||
WriteLine($"Using {path} database file.");
|
||||
|
||||
optionsBuilder.UseSqlite($"Filename={path}");
|
||||
}
|
||||
else
|
||||
{
|
||||
string connection = "Data Source=.;" +
|
||||
"Initial Catalog=Northwind;" +
|
||||
"Integrated Security=true;" +
|
||||
"MultipleActiveResultSets=true;";
|
||||
|
||||
optionsBuilder.UseSqlServer(connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Defining the Category and Product entity classes
|
||||
|
||||
#!csharp
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System.ComponentModel.DataAnnotations; // [Required], [StringLength]
|
||||
using System.ComponentModel.DataAnnotations.Schema; // [Column]
|
||||
|
||||
public class Category
|
||||
{
|
||||
// these properties map to columns in the database
|
||||
public int CategoryId { get; set; }
|
||||
public string? CategoryName { get; set; }
|
||||
|
||||
[Column(TypeName = "ntext")]
|
||||
public string? Description { get; set; }
|
||||
|
||||
// defines a navigation property for related rows
|
||||
public virtual ICollection<Product> Products { get; set; }
|
||||
|
||||
public Category()
|
||||
{
|
||||
// to enable developers to add products to a Category we must
|
||||
// initialize the navigation property to an empty collection
|
||||
this.Products = new HashSet<Product>();
|
||||
}
|
||||
}
|
||||
|
||||
public class Product
|
||||
{
|
||||
public int ProductId { get; set; } // primary key
|
||||
|
||||
[Required]
|
||||
[StringLength(40)]
|
||||
public string ProductName { get; set; } = null!;
|
||||
|
||||
[Column("UnitPrice", TypeName = "money")]
|
||||
public decimal? Cost { get; set; } // property name != column name
|
||||
|
||||
[Column("UnitsInStock")]
|
||||
public short? Stock { get; set; }
|
||||
|
||||
public bool Discontinued { get; set; }
|
||||
|
||||
// these two define the foreign key relationship
|
||||
// to the Categories table
|
||||
public int CategoryId { get; set; }
|
||||
public virtual Category Category { get; set; } = null!;
|
||||
}
|
||||
206
notebooks/Chapter11.dib
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
#!markdown
|
||||
|
||||
# Chapter 11 - Querying and Manipulating Data Using LINQ
|
||||
- Writing LINQ expressions
|
||||
- Working with sets using LINQ
|
||||
- Using LINQ with EF Core
|
||||
- Sweetening LINQ syntax with syntactic sugar
|
||||
- Using multiple threads with parallel LINQ
|
||||
- Creating your own LINQ extension methods
|
||||
- Working with LINQ to XML
|
||||
|
||||
#!markdown
|
||||
|
||||
# Writing LINQ expressions
|
||||
|
||||
#!markdown
|
||||
|
||||
## Understanding deferred execution
|
||||
|
||||
#!csharp
|
||||
|
||||
using static System.Console;
|
||||
|
||||
#!csharp
|
||||
|
||||
// a string array is a sequence that implements IEnumerable<string>
|
||||
string[] names = new[] { "Michael", "Pam", "Jim", "Dwight",
|
||||
"Angela", "Kevin", "Toby", "Creed" };
|
||||
|
||||
WriteLine("Deferred execution");
|
||||
|
||||
// Question: Which names end with an M?
|
||||
// (written using a LINQ extension method)
|
||||
var query1 = names.Where(name => name.EndsWith("m"));
|
||||
|
||||
// Question: Which names end with an M?
|
||||
// (written using LINQ query comprehension syntax)
|
||||
var query2 = from name in names where name.EndsWith("m") select name;
|
||||
|
||||
#!csharp
|
||||
|
||||
// Answer returned as an array of strings containing Pam and Jim
|
||||
string[] result1 = query1.ToArray();
|
||||
|
||||
// Answer returned as a list of strings containing Pam and Jim
|
||||
List<string> result2 = query2.ToList();
|
||||
|
||||
// Answer returned as we enumerate over the results
|
||||
foreach (string name in query1)
|
||||
{
|
||||
WriteLine(name); // outputs Pam
|
||||
//names[2] = "Jimmy"; // change Jim to Jimmy
|
||||
// on the second iteration Jimmy does not end with an M
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Filtering entities with Where
|
||||
|
||||
#!csharp
|
||||
|
||||
static bool NameLongerThanFour(string name)
|
||||
{
|
||||
return name.Length > 4;
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
WriteLine("Writing queries");
|
||||
|
||||
// var query = names.Where(
|
||||
// new Func<string, bool>(NameLongerThanFour));
|
||||
|
||||
// var query = names.Where(NameLongerThanFour);
|
||||
|
||||
IOrderedEnumerable<string> query = names
|
||||
.Where(name => name.Length > 4)
|
||||
.OrderBy(name => name.Length)
|
||||
.ThenBy(name => name);
|
||||
|
||||
foreach (string item in query)
|
||||
{
|
||||
WriteLine(item);
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Filtering by type
|
||||
|
||||
#!csharp
|
||||
|
||||
WriteLine("Filtering by type");
|
||||
|
||||
List<Exception> exceptions = new()
|
||||
{
|
||||
new ArgumentException(),
|
||||
new SystemException(),
|
||||
new IndexOutOfRangeException(),
|
||||
new InvalidOperationException(),
|
||||
new NullReferenceException(),
|
||||
new InvalidCastException(),
|
||||
new OverflowException(),
|
||||
new DivideByZeroException(),
|
||||
new ApplicationException()
|
||||
};
|
||||
|
||||
#!csharp
|
||||
|
||||
IEnumerable<ArithmeticException> arithmeticExceptionsQuery =
|
||||
exceptions.OfType<ArithmeticException>();
|
||||
|
||||
foreach (ArithmeticException exception in arithmeticExceptionsQuery)
|
||||
{
|
||||
WriteLine(exception);
|
||||
}
|
||||
|
||||
#!markdown
|
||||
|
||||
## Working with sets and bags using LINQ
|
||||
|
||||
#!csharp
|
||||
|
||||
static void Output(IEnumerable<string> cohort, string description = "")
|
||||
{
|
||||
if (!string.IsNullOrEmpty(description))
|
||||
{
|
||||
WriteLine(description);
|
||||
}
|
||||
Write(" ");
|
||||
WriteLine(string.Join(", ", cohort.ToArray()));
|
||||
WriteLine();
|
||||
}
|
||||
|
||||
#!csharp
|
||||
|
||||
string[] cohort1 = new[]
|
||||
{ "Rachel", "Gareth", "Jonathan", "George" };
|
||||
string[] cohort2 = new[]
|
||||
{ "Jack", "Stephen", "Daniel", "Jack", "Jared" };
|
||||
string[] cohort3 = new[]
|
||||
{ "Declan", "Jack", "Jack", "Jasmine", "Conor" };
|
||||
Output(cohort1, "Cohort 1");
|
||||
Output(cohort2, "Cohort 2");
|
||||
Output(cohort3, "Cohort 3");
|
||||
Output(cohort2.Distinct(), "cohort2.Distinct()");
|
||||
/* DistinctBy is only available in .NET 6 and later
|
||||
Output(cohort2.DistinctBy(name => name.Substring(0, 2)),
|
||||
"cohort2.DistinctBy(name => name.Substring(0, 2)):");
|
||||
*/
|
||||
Output(cohort2.Union(cohort3), "cohort2.Union(cohort3)");
|
||||
Output(cohort2.Concat(cohort3), "cohort2.Concat(cohort3)");
|
||||
Output(cohort2.Intersect(cohort3), "cohort2.Intersect(cohort3)");
|
||||
Output(cohort2.Except(cohort3), "cohort2.Except(cohort3)");
|
||||
Output(cohort1.Zip(cohort2,(c1, c2) => $"{c1} matched with {c2}"),
|
||||
"cohort1.Zip(cohort2)");
|
||||
|
||||
#!markdown
|
||||
|
||||
# Using LINQ with EF Core
|
||||
|
||||
.NET Interactive Notebooks for SQL Server does not yet support custom EF Core models.
|
||||
|
||||
#!markdown
|
||||
|
||||
# Using multiple threads with parallel LINQ
|
||||
|
||||
To see it in action, we will start with some code that only uses a single thread to calculate Fibonacci numbers for 45 integers.
|
||||
|
||||
#!csharp
|
||||
|
||||
using System.Diagnostics;
|
||||
|
||||
Stopwatch watch = new();
|
||||
|
||||
//Write("Press ENTER to start. ");
|
||||
//ReadLine();
|
||||
watch.Start();
|
||||
|
||||
int max = 45;
|
||||
|
||||
IEnumerable<int> numbers = Enumerable.Range(start: 1, count: max);
|
||||
|
||||
WriteLine($"Calculating Fibonacci sequence up to {max}. Please wait...");
|
||||
|
||||
int[] fibonacciNumbers = numbers.AsParallel()
|
||||
.Select(number => Fibonacci(number))
|
||||
.OrderBy(number => number)
|
||||
.ToArray();
|
||||
|
||||
watch.Stop();
|
||||
WriteLine("{0:#,##0} elapsed milliseconds.",
|
||||
arg0: watch.ElapsedMilliseconds);
|
||||
|
||||
Write("Results:");
|
||||
foreach (int number in fibonacciNumbers)
|
||||
{
|
||||
Write($" {number}");
|
||||
}
|
||||
|
||||
static int Fibonacci(int term) =>
|
||||
term switch
|
||||
{
|
||||
1 => 0,
|
||||
2 => 1,
|
||||
_ => Fibonacci(term - 1) + Fibonacci(term - 2)
|
||||
};
|
||||
BIN
notebooks/Northwind.db
Normal file
8722
notebooks/Northwind4SQLite.sql
Normal file
BIN
notebooks/images/categories-thumbnail.jpeg
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
notebooks/images/categories.jpeg
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
notebooks/images/category1-thumbnail.jpeg
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
notebooks/images/category1.jpeg
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
notebooks/images/category2-thumbnail.jpeg
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
notebooks/images/category2.jpeg
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
notebooks/images/category3-thumbnail.jpeg
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
notebooks/images/category3.jpeg
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
notebooks/images/category4-thumbnail.jpeg
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
notebooks/images/category4.jpeg
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
notebooks/images/category5-thumbnail.jpeg
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
notebooks/images/category5.jpeg
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
BIN
notebooks/images/category6-thumbnail.jpeg
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
notebooks/images/category6.jpeg
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
notebooks/images/category7-thumbnail.jpeg
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
notebooks/images/category7.jpeg
Normal file
|
After Width: | Height: | Size: 2 MiB |
BIN
notebooks/images/category8-thumbnail.jpeg
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
notebooks/images/category8.jpeg
Normal file
|
After Width: | Height: | Size: 1.6 MiB |