One of the great things about learning Microsoft stuff is that the documentation guide is very careful.

How to read documents

Sorry, sorry, Microsoft’s C# document structure is too ambiguous and confusing (the document structure, not the document itself), you will hunger when you read it.

Why do you say that?

First let’s look at the structure:Can you guess where the correct tutorial is? Did you think it was the big “tutorial”?

However, if you click on the meeting, you find:It’s obvious from the title that this is not something for beginners, so where is the right first tutorial? Here:

So guess where the second tutorial is? Yeah, in the tutorial under “Basics.”

So what is the correct full tutorial sequence?

  1. First, finish the tutorial under “Getting Started”.
  2. Then finish the tutorial under “Basics”.
  3. Then finish the tutorial under “new features in C#”.
  4. Then finish the bare “Tutorials” section on the outermost layer.

Yes, the outermost tutorial actually refers to the last tutorial. I believe that anyone would find this structure ambiguous.

The first five sections of this article (from C# to program structure) are an introduction from the “getting started” section. After reading “Introduction,” I realized that the next subsection, “genres,” was not for learning at all. It took me a long time to find what a true beginner should read, which is the order just described above. It’s really hard. Microsoft is guilty.

1. C# language introduction

C# is an object-oriented, component-oriented language. There’s nothing more to say.

2. The.net is introduced

C# runs on.net.

  1. Before introducing.NET, you should first introduce the CLI (not the command-line CLI, but Common Language Infrastructure), which is a standard that defines a cross-language runtime environment.
  2. Then we had the CLR (Common Language Runtime), an implementation of the CLI developed by Microsoft. That is, the CLR is a cross-language runtime environment. The CLI is standard and defines a cross-language runtime environment; CLR is the implementation, it is a cross-language runtime environment)
  3. .NET, on the other hand, is a development platform with the CLR, a bunch of class libraries, and a bunch of other crap. .net is a catch-all.
  4. Since.net includes CLR, that means.net is not bound to C#. You can write programs in.net using any language supported by the CLR, such as F#, etc.

After the source code is written on.NET, it is compiled into IL. IL code and resources are stored in assemblies with extensions usually DLL.

When C# programs are executed, they are loaded onto the CLR (hence.net is a must in order to run C# programs, which is why many programs, especially games, often require you to download versions of the.net framework). The CLR then performs a just-in-time (JIT) compilation of the IL code, compiling the machine instructions. The CLR provides functions such as garbage collection, exception handling, resource management, and so on.

IL is an intermediate language, so it can interact with IL code compiled from other.net versions of F#, C++, etc. An assembly can have many modules written in different languages that reference each other.

In addition to CLR,.NET also provides class libraries. It’s easy to understand if you’re familiar with other languages. It contains various libraries such as input/output, Web frameworks, string controls, and so on, all divided into corresponding namespaces. Of course, in addition to the official supply, there are others or their own third-party libraries.

Look at this picture (please point out any mistakes/ambiguities) :

3. Hello World

Code first:

using System;

class Hello {
    static void Main() {
        Console.WriteLine("Hello, World!"); }}Copy the code

Even though it is just a HelloWorld, there are many ways to say it:

  1. Using System on the first line; Indicates that the System namespace is used. A namespace can contain types or other namespaces. Console in line 5, for example, is the class contained in SystemSystem.ConsoleIn addition, the System also has other namespaces such asIOCollections. Abbreviate when referring to namespace, as the fifth line should have beenSystem.Console.WriteLine, but since we have the reference to the first line, so the shorthand isConsole.WriteLineThat’s ok.
  2. We have a class called Hello, and then we have a method in it called Main, which is decorated with a static modifier, which is called a static method. Static methods do not require an instance to be used. Instance methods need to be run on the instance, which can be referred to by this. In addition, Main is a program entry specified by C#. When the program runs, it automatically finds this method to run, so it must have this method.
  3. Console is a class in which WriteLine is its static method. The Cosole class is provided by the standard library, which is automatically referenced by the compiler by default.

4. Types and variables

C# has two types. Value type and reference type. Variables of value type contain data directly, while variables of reference type store references to data.

Here’s a breakdown of types:

  • Value type:
    • Simple types:
      • Signed integers: sbyte, short, int, long
      • Unsigned integer: byte, ushory, uint, ulONG
      • Unicode character: char, representing UTF-16 code unit
      • IEEE binary floating point numbers: float, double
      • High precision decimal floating point number: Decimal
      • Boolean value: bool
    • Enumeration types:
      • enum E {… } format of a user-defined type.
    • Structure type:
      • struct S {… } format of a user-defined type
    • A value type that can be null
      • An extension of all other value types, containing all values and NULL for that value type
    • Primitive value types:
      • (T1, T2, T3…) The user-defined type of the format
  • Reference type:
    • Class type :(a bit of a mouthful, break the sentence as class type)
      • The base class for all types, including value types: Object
      • Unicode String: String, which represents the SEQUENCE of UTF-16 code units
      • class C {… } format of a user-defined type
    • Interface type:
      • interface I {… } format of a user-defined type
    • Array type:
      • One-dimensional, multidimensional, or interleaved: int[], int[,] or int[][]
    • Delegate type:
      • delegate int D(…) The user-defined type of the format
  1. Struct types and class types can both contain data members and function members. The difference is that structs are value types and do not require heap allocation. In addition, structs do not support user-specified inheritance. (Why not just say inheritance is not supported, instead of user-specified inheritance? Since all structs implicitly inherit from object, virtually all types inherit from object.)
  2. An interface can inherit from multiple other interfaces. Classes and structs can also implement multiple interfaces simultaneously.
  3. A delegate can have a method assigned to a variable, or even as an argument to a function. Similar to function types provided by functional languages.
  4. Class, struct, interface, delegate all support generics.

Types that can be null need not be defined. For any type T that does not have a null value, we can add one, okay? Become a T? Type. This variable can then be null.

C# uses a uniform type system, so all types, including value types, can be considered object types, and all types derive directly or indirectly from object types.

Value types can be used as reference types by boxing and unboxing them.

int i = 123;
object o = i; / / packing
int j = (int)o; / / split open a case
Copy the code

Once boxed, the values are copied into the box. The box is checked to see if it has the correct value type when unpacking it.

C#’s unified type system actually means referring to value types as reference types on demand. If you have a library that uses Object, you can actually use both value and reference types.

5. Program structure

Key program structures in C# include programs, namespaces, types, members, and assemblies. Programs declare types, which contain members and are collated into namespaces. Types include classes, interfaces, and structures. Members include fields, methods, properties, and events. The compiled C# is actually packaged into an assembly, either exe or DLL, depending on whether it is packaged as an application or a library.

6. Usage of strings

Here we begin the formal tutorial.

6.1 Basic Attributes

  1. String can be concatenated with +.
  2. String interpolation. Similar to template strings in other languages. Format for$"hello {yourName}"Where yourName is the variable.
  3. The string has the Length attribute, which gives you the Length of the string. Format foryourName.Length.

6.2 String Operations

Make some changes to the string.

  1. Trim, TrimStart, TrimEnd can be removed from trailing Spaces.
  2. The Replace method replaces strings. Replace all of them.
  3. ToUpper and ToLower are all uppercase and lowercase.

The operation string method returns a new string.

6.3 Searching for Strings

  1. The Contains method returns whether the string Contains a substring as an argument. The return value is a Boolean value.
  2. StartsWith and EndsWith return whether the string begins or EndsWith a substring.

7. Digital

  1. Int.MaxValue and int.MinValue represent the maximum and minimum (i.e., negative maximum) that an int type can carry, respectively.
  2. Int.MaxValue + 1 == int.MinValue.
  3. Decimals are typically double instead of float (the best example is when the compiler assumes a double for a decimal constant). Double has a huge range. Up to 10 to the power of 308 (also viewed by double-.maxValue). Longer than long (and much longer).
  4. But double is still not accurate enough. We’re still only going to have 15 decimal places. For more decimal places, use the decimal type. The maximum minimum range of decimal types is much lower than that of double, but is more precise.
decimal a = 1.0M;

If M is not added, the compiler will treat it as a double
Console.WriteLine(1.0/3); / / 0.333333333333333
Console.WriteLine(1.0M/3); / / 0.3333333333333333333333333333
Copy the code

8. Branches and loops

There’s nothing to tell.

List of 9.

There’s a lot to talk about, so let’s start with a list of code:

var names = new List<string> {"<name>"."asshole"."joe"};

foreach (var name in names) {
    Console.WriteLine($"Hello {name}");
}
Copy the code

The output is:

  1. List<T>Type, which stores a series of elements of type T.
  2. Foreach provides a convenient way to traverse a list. (Of course, var can be replaced with string only if you are not sure of the type.)

9.1 Modifying a List

Add Adds elements. Remove Removes elements. The Count attribute shows the number of elements in the list. (Not Length) Actually, there are two ways to get a list Length.

9.2 Count attribute VS Count() method.

The two methods behave in the same way. To put it bluntly, Count is faster than Count() because it does no type checking.

9.3 Searching and sorting lists

  1. The IndexOf() method searches for the index position of the element and returns -1 if it does not exist.
  2. Name.sort () sorts the list. Will change the original list!! This method does not return a value!

10. How do I display command line parameters

In fact, if you’re playing games, you don’t have to learn this. But anyway the tutorial is only a few words, look at it.

The command-line arguments are passed as an array to the Main method. As follows:

static void Main(string[] args){}Copy the code

That’s it. That’s it.

11. The class description

As an OOP language, classes are certainly one of the most important concepts. So now let’s try to create a class that represents a bank account.

using System;

namespace classes {
    public class BankAccount {
        public string Number {get; }public string Owner {get; set; }public decimal Balance {get; }public void MakeDeposit (decimal amount, DateTime date, string note){}public void MakeWithdrawal (decimal amount, DateTime date, string note){}}}Copy the code
  • Namespaces are used to organize code, like the little code we’re writing now, with a namespace.
  • Public indicates whether it can be referenced by other files.

BanckAccount class:

  1. The first three lines are properties, which define some validation and other rules. Get, set indicates the read and write permissions. If you want the property to be read-only, you can omit the set.
  2. The last two lines are methods.

11.1 Adding an Account

Let’s add a function to add accounts. That’s instantiation. So here’s constructor. Constructor is a member of the same class name. To initialize an instance:

The constructor method does not return the same name as the class name. The constructor method does not return the same name as the class name. The constructor method does not return the same name as the class name.

Try the results:

11.1.1 Account Number (Static Modifier)

We have a property called Number, which should not be supplied by the user but generated by code.

An easy way to do this is to start with an initial 10-digit number and give it + 1 each time you add an account.

So let’s do it with this code:Details abound to explain:

  1. Private, because it’s only used in this class, just as an initial value.
  2. Static, so that the number is shared. If static is not added, the new account will be 1000000001 every time, because the number will be regenerated. But if you add static, the property is bound to the class, not to the instance. In other words, static-modified properties allow us to share data between instances.

We can then change the constructor to this:

  • Because now in the class, so called directly initialAccountNumber can, if is on the outside, with now. InitialAccountNumber. Of course, since we’re private, we can’t even call it from outside.

Test the effect:

11.2 Creating deposits and withdrawals

Simply changing Balance is neither interesting nor rational. Let’s start by creating a class called Transaction. Use this to record changes in Balancce.

Then we create a property that stores all historical transactions for each account.

Then we modify the Balance property. Make it equal to the computed result of all transactions.

Then we finally start writing methods, which introduce exception:

We then need to modify constructor since the initialization of balance also needs to change (at this point balance can be removed and set become read-only) :

Date.Now gets the current time. Note that this is an attribute, not a method.

11.3 test

Use try catch to catch errors:

It looks similar, but there is no mention of uncaught exceptions. And this mistake was printed on my own initiative.

11.4 Log All Transactions

Look at the code:The AppendLine method of type StringBuilder automatically adds a space after each line. Then we adjust the indentation with tabs. The test results are as follows:

Inheritance and polymorphism

OOP has four important features. Abstraction, Encapsulation, Inheritance, Polymorphism. We talked about the first two in the last video, and the last two in this video.

Still take the bank account number class just now to explain.

12.1 inheritance

We need to create three new bank account types. Savings cards, credit cards and gift cards. First we create them and inherit from the previous class:

  1. Each new class already has the same properties and methods as BankAccount.
  2. You can see the error because we need the constructor. And this constructor initializes not only the current class, but also the base class.

Look at the following code:

  1. The same format is used in the constructor, but the inherited class is replaced with base instead of the base class name.
  2. Arguments in base are not typed, because they are called, not declared.
  3. The idea is that the constructor calls the base class constructor, and the parameters are taken directly from the current constructor.
  4. Sometimes the base class may have more than one constructor, and this syntax allows you to choose which constructor to use.

12.2 polymorphic

Let’s say each card does something at the end of the month, but each card does something different. So let’s use polymorphism to do this.

First we go to the BankAccount class and add the following virtual methods:

  • The virtual keyword indicates that the method may later be reimplemented by the inherited class. Since it is possible, you can also write some code in the base class and not overwrite it.
  • Use the override keyword to override methods in inherited classes.
  • You can also change virtual to abstract to force the method to be overridden by inherited classes. (But then the base class itself would have to be abstract, more on that later.)

Methods of the base class can also be called from an inherited class:

But we don’t need to use the first two cards now, the implementation code is as follows:

For gift cards, there are more rules, so we need to change something else. For example, let’s change the constructor first:Here, because we have only one sentence, we leave out the braces and use the => lambda operator.

Then we’ll rewrite the MonthlyTask method:

Test gift and savings cards:

But when it came time to test the credit card, something went wrong:This is resonable because the BankAccount limit does not allow you to withdraw more money than Banlance. So we need to modify BankAccount:

  • The difference between a readonly property and a property that only gives a GET is that. The former can only be assigned at the constructor stage and cannot be changed once initialized. The latter can be changed within the class. But it’s viewed externally as a read-only property.
  • Here we have two constructors. The first constructor takes three arguments; The second takes both arguments and uses them simultaneously:this()Syntax to invoke the previous constructor. Then here we just need to call the first constructor (in the form of the third argument of 0) and do nothing else, so use:this() {}.

Then we modify the MakeWithdrawal:

And then in CreditCardAccount we can change it to something like this:

12.2.1 protocted

Now, to allow a credit card to exceed the creditLimit, use the Virtual method. In BankAccount, change the MakeWithdrawal method:

public void MakeWithdrawal(decimal amount, DateTime date, string note)
{
    if (amount <= 0)
    {
        throw new ArgumentOutOfRangeException(nameof(amount), "Amount of withdrawal must be positive");
    }
    var overdraftTransaction = CheckWithdrawalLimit(Balance - amount < minimumBalance);
    var withdrawal = new Transaction(-amount, date, note);
    allTransactions.Add(withdrawal);
    if(overdraftTransaction ! =null)
        allTransactions.Add(overdraftTransaction);
}

protected virtual Transaction? CheckWithdrawalLimit(bool isOverdrawn)
{
    if (isOverdrawn)
    {
        throw new InvalidOperationException("Not sufficient funds for this withdrawal");
    }
    else
    {
        return default; }}Copy the code

I write it here because my new project uses C# 7.3, and only C# 8.0 has nullable reference types support.

In addition to modifying the MakeWithdrawal method, a new method has been added. The modifiers before this method are protected and virtual.

  • Protected and public are the same, private means that the method can only be called by an inherited class.
  • Virtual means that the method can be overridden by inherited classes.

This adds up to only being called by inherited classes and can be overridden.

So we override it in CreditCardAccount:

public override Transaction? CheckWithdrawalLimit(bool isOverdrawn)
    => isOverdrawn
    ? new Transaction(- 20, DateTime.Now, "Apply overdraft fee")
    : default;
    
Copy the code

In this way, by separating the code into a virtual method and then overriding, you can behave differently.