C# & .NET Core Notes!

02 Apr 2022 Note and CSharp

C# 是专为公共语言基础结构(CLI)设计的。CLI 由可执行代码和运行时环境组成,允许在不同的计算机平台和体系结构上使用各种高级语言。本文会重点记录 C# 与 Java/C++ 相似却不太一样的点。

一个 C# 程序永远以 using System; 开头,类似下面这样定义namespace, class 和主函数 static void Main(string[] args)。大小写敏感,有很多和 C++ 一样的语法性质。编译时需要考虑方法体的花括号{} 和分号;,而非空格和换行符。因为 Python 写得太多了所以像提醒小朋友一样提醒一下自己。

一个例子:

using System;

namespace CalculatorApplication
{
   class NumberManipulator
   {
      public int FindMax(int num1, int num2)
      {
         /* 局部变量声明 */
         int result;

         if (num1 > num2)
            result = num1;
         else
            result = num2;

         return result;
      }
      static void Main(string[] args)
      {
         /* 局部变量定义 */
         int a = 100;
         int b = 200;
         int ret;
         NumberManipulator n = new NumberManipulator();

         //调用 FindMax 方法
         ret = n.FindMax(a, b);
         Console.WriteLine("最大值是: {0}", ret );
         Console.ReadLine();
      }
   }
}

C# 与 .NET 的关系[1]

.Net 框架是一个创新的平台,能帮您编写出下面类型的应用程序:

.Net 框架应用程序是多平台的应用程序。框架的设计方式使它适用于下列各种语言:C#、C++、Visual Basic、Jscript、COBOL 等等。所有这些语言可以访问框架,彼此之间也可以互相交互。

.Net 框架由一个巨大的代码库组成,用于 C# 等客户端语言。

一些 .Net 框架的组件:

公共语言运行库(Common Language Runtime - CLR)
.Net 框架类库(.Net Framework Class Library)
公共语言规范(Common Language Specification)
通用类型系统(Common Type System)
元数据(Metadata)和组件(Assemblies)
Windows 窗体(Windows Forms)
ASP.Net 和 ASP.Net AJAX
ADO.Net
Windows 工作流基础(Windows Workflow Foundation - WF)
Windows 显示基础(Windows Presentation Foundation)
Windows 通信基础(Windows Communication Foundation - WCF)
LINQ

简单地说[2]:

1、.NET

.NET是一个平台,抽象的平台概念。

实现形式是库

①定义了基本的类型(通用类型系统CTS,common type system)。 ②包含.net公共语言运行库(CLR,common language runtime,负责管理用.net库开发的所有应用程序的运行)。

核心是.net framework

NET Framework包括两个关键组成元素:

a.Common Language Runtime,公共语言运行时(CLR)-提供内在管理,代码安全性检测等功能。

b..NET Framework Class Library,.NET框架类库(FLC)-提供大量应用类库,提高开发效率。

2、C#

  C#是一个程序设计语言,仅仅是一个语言,是运行在.net CLR上的,用于创建应用程序的高级语言。

3、ASP.NET是一个网站开发的技术,仅仅是.NET框架中的一个应用模型。   ASP.NET 是用于生成基于Web的应用程序的内容丰富的编程框架。

p.s. [3]

C#本身是一门语言,它是用于生成面向.NET环境的代码,但其并不是.NET的一部分。换言之,C#编写的代码总是运行在.NET Framework中。而且,在很多时候,C#的特定功能依赖于.NET。比如,在C#中声明一个int类型,实际上是.NET中System.Int32类的一个实例。它是专门为与Microsoft的.NET Framework一起使用而设计的。而且.NET支持的一些特性,C#并不支持。而C#语言支持的另一些特性,.NET却不支持(例如运算符重载)。

标识符

标识符是用来识别类、变量、函数或任何其它用户定义的项目。在 C# 中,类的命名必须遵循如下基本规则:

变量类型

在 C# 中,变量分为以下几种类型:

值类型变量可以直接分配给一个值。From System.ValueType. e.g. int , float, char etc.

引用类型不包含存储在变量中的实际数据,但它们包含对变量的引用。

换句话说,它们指的是一个内存位置。使用多个变量时,引用类型可以指向一个内存位置。如果内存位置的数据是由一个变量改变的,其他变量会自动反映这种值的变化。内置的 引用类型有:object、dynamic 和 string。

object 即 System.Object,是 C# 通用类型系统(Common Type System - CTS)中所有数据类型的终极基类。

object obj;
obj = 100; // 这是装箱

可以存储任何类型的值在动态数据类型变量中。这些变量的类型检查是在运行时发生的。

dynamic <variable_name> = value;

e.g.

dynamic a = 10;

字符串(String)类型是 System.String 类的别名。它是从对象(Object)类型派生的。字符串(String)类型的值可以通过两种形式进行分配:引号和 @引号。

C# string 字符串的前面可以加 @(称作”逐字字符串”)将转义字符(\)当作普通字符对待,比如:

string str = @"C:\Windows";

等价于:

string str = "C:\\Windows";

@ 字符串中可以任意换行,换行符及缩进空格都计算在字符串长度之内。

指针类型变量存储另一种类型的内存地址。

声明指针类型的语法:

type* identifier;

接受来自用户的值

System 命名空间中的 Console 类提供了一个函数 ReadLine(),用于接收来自用户的输入,并把它存储到一个变量中。 例如:

int num;
num = Convert.ToInt32(Console.ReadLine());

函数 Convert.ToInt32() 把用户输入的数据转换为 int 数据类型,因为 Console.ReadLine() 只接受字符串格式的数据。

常量表示法

整数常量

整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,没有前缀则表示十进制。

整数常量也可以有后缀,可以是 U 和 L 的组合,其中,U 和 L 分别表示 unsigned 和 long。后缀可以是大写或者小写,多个后缀以任意顺序进行组合。

这里有一些整数常量的实例:

212         /* 合法 */
215u        /* 合法 */
0xFeeL      /* 合法 */
078         /* 非法:8 不是一个八进制数字 */
032UU       /* 非法:不能重复后缀 */

以下是各种类型的整数常量的实例:

85         /* 十进制 */
0213       /* 八进制 */
0x4b       /* 十六进制 */
30         /* int */
30u        /* 无符号 int */
30l        /* long */
30ul       /* 无符号 long */

浮点常量

一个浮点常量是由整数部分、小数点、小数部分和指数部分组成。您可以使用小数形式或者指数形式来表示浮点常量。

这里有一些浮点常量的实例:

3.14159       /* 合法 */
314159E-5L    /* 合法 */
510E          /* 非法:不完全指数 */
210f          /* 非法:没有小数或指数 */
.e55          /* 非法:缺少整数或小数 */

其他运算符

sizeof()	返回数据类型的大小。	sizeof(int),将返回 4.
typeof()	返回 class 的类型。	typeof(StreamReader);
&	返回变量的地址。	&a; 将得到变量的实际地址。
*	变量的指针。	*a; 将指向一个变量。
? :	条件表达式	如果条件为真 ? 则为 X : 否则为 Y
is	判断对象是否为某一类型。	If( Ford is Car) // 检查 Ford 是否是 Car 类的一个对象。
as	强制转换,即使转换失败也不会抛出异常。	Object obj = new StringReader("Hello");
StringReader r = obj as StringReader;

封装 Encapsulation

封装(encapsulation) 被定义为”把一个或多个项目封闭在一个物理的或者逻辑的包中”。在面向对象程序设计方法论中,封装是为了防止对实现细节的访问。

Encapsulation is defined ‘as the process of enclosing one or more items within a physical or logical package’.

Encapsulation, in object oriented programming methodology, prevents access to implementation details.

抽象和封装是面向对象程序设计的相关特性。抽象允许相关信息可视化,封装则使开发者实现所需级别的抽象

Abstraction and encapsulation are related features in object oriented programming. Abstraction allows making relevant information visible and encapsulation enables a programmer to implement the desired level of abstraction.

C# 封装根据具体的需要,设置使用者的访问权限,并通过 访问修饰符 来实现。

Encapsulation is implemented by using access specifiers. An access specifier defines the scope and visibility of a class member.

一个 访问修饰符 定义了一个类成员的范围和可见性。C# 支持的访问修饰符如下所示:

public:所有对象都可以访问;

private:对象本身在对象内部可以访问(Only functions of the same class can access its private members);

如果没有指定访问修饰符,则使用类成员的默认访问修饰符,即为 private。

protected:只有该类对象及其子类对象可以访问(allows a child class to access the member variables and member functions of its base class)

internal:同一个程序集的对象可以访问;

Internal 访问说明符允许一个类将其成员变量和成员函数暴露给当前程序(current assembly)中的其他函数和对象。换句话说,带有 internal 访问修饰符的任何成员可以被定义在该成员所定义的应用程序内的任何类或方法访问(within the application in which the member is defined)。

protected internal:访问限于当前程序集或派生自包含类的类型。

F2

方法的参数传递

e.g.

using System;
namespace CalculatorApplication
{
class NumberManipulator
{
    public void swap(ref int x, ref int y)
    {
        int temp;
        temp = x; /* 保存 x 的值 */
        x = y;    /* 把 y 赋值给 x */
        y = temp; /* 把 temp 赋值给 y */
    }

    static void Main(string[] args)
    {
        NumberManipulator n = new NumberManipulator();
        /* 局部变量定义 */
        int a = 100;
        int b = 200;

        n.swap(ref a, ref b);

The out keyword causes arguments to be passed by reference. It makes the formal parameter an alias for the argument, which must be a variable. In other words, any operation on the parameter is made on the argument. It is like the ref keyword, except that ref requires that the variable be initialized before it is passed. It is also like the in keyword, except that in does not allow the called method to modify the argument value. To use an out parameter, both the method definition and the calling method must explicitly use the out keyword.

C# 可空类型(Nullable)

C# 提供了一个特殊的数据类型,nullable 类型(可空类型),可空类型可以表示其基础值类型正常范围内的值,再加上一个 null 值。

例如,Nullable< Int32 >,读作”可空的 Int32”,可以被赋值为 -2,147,483,648 到 2,147,483,647 之间的任意值,也可以被赋值为 null 值。类似的,Nullable< bool > 变量可以被赋值为 true 或 false 或 null。

e.g.

int? num1 = null;
int? num2 = 45;
double? num3 = new double?();
double? num4 = 3.14157;

Null 合并运算符( ?? )

Null 合并运算符用于定义可空类型和引用类型的默认值。Null 合并运算符为类型转换定义了一个预设值,以防可空类型的值为 Null。Null 合并运算符把操作数类型隐式转换为另一个可空(或不可空)的值类型的操作数的类型。

如果第一个操作数的值为 null,则运算符返回第二个操作数的值,否则返回第一个操作数的值。

double? num1 = null;
double num3;
num3 = num1 ?? 5.34;      // num1 如果为空值则返回 5.34

C# 结构的特点

在 C# 中的结构与传统的 C 或 C++ 中的结构不同。C# 中的结构有以下特点:

结构可带有方法、字段、索引、属性、运算符方法和事件。

结构可定义构造函数,但不能定义析构函数。但是,您不能为结构定义无参构造函数。无参构造函数(默认)是自动定义的,且不能被改变。

与类不同,结构不能继承其他的结构或类。

结构不能作为其他结构或类的基础结构。

结构可实现一个或多个接口。

结构成员不能指定为 abstract、virtual 或 protected。

当您使用 New 操作符创建一个结构对象时,会调用适当的构造函数来创建结构。与类不同,结构可以不使用 New 操作符即可被实例化。

如果不使用 New 操作符,只有在所有的字段都被初始化之后,字段才被赋值,对象才被使用。

类 Class vs 结构 Struct

类和结构有以下几个基本的不同点:

  1. 类是引用类型,它在栈中分配空间,结构是值类型,它在堆中分配空间,栈中保存的只是引用。

  2. 结构不支持继承。

  3. 结构不能声明默认的构造函数。

结构中不能实例属性或字段初始值设定。

Ex. 首先明确,类的对象是存储在堆空间中,结构存储在栈中。堆空间大,但访问速度较慢,栈空间小,访问速度相对更快。故而,当我们描述一个轻量级对象的时候,结构可提高效率,成本更低。当然,这也得从需求出发,假如我们在传值的时候希望传递的是对象的引用地址而不是对象的拷贝,就应该使用类了。

枚举(Enum)

枚举是一组命名整型常量,使用 enum 关键字声明。

C# 枚举是值类型。换句话说,枚举包含自己的值,且不能继承或传递继承。

枚举列表中的每个符号代表一个整数值,一个比它前面的符号大的整数值。默认情况下,第一个枚举符号的值是 0。

enum Day { Sun, Mon, Tue, Wed, Thu, Fri, Sat };

static void Main()
{
    int x = (int)Day.Sun;
    int y = (int)Day.Fri;
    Console.WriteLine("Sun = {0}", x); // Sun = 0
    Console.WriteLine("Fri = {0}", y); // Fri = 5
}

OOP in C#[5]

OOP stands for Object-Oriented Programming.

Procedural programming is about writing procedures or methods that perform operations on the data, while object-oriented programming is about creating objects that contain both data and methods.

Object-oriented programming has several advantages over procedural programming:

Tip: The “Don’t Repeat Yourself” (DRY) principle is about reducing the repetition of code. You should extract out the codes that are common for the application, and place them at a single place and reuse them instead of repeating it.

继承 Inheritance (Derived and Base Class)

To inherit from a class, use the : symbol.

<访问修饰符> class <基类>
{
...
}
class <派生类> : <基类>
{
...
}

e.g.

class Vehicle  // base class (parent) 
{
    public string brand = "Ford";  // Vehicle field
    public void honk()             // Vehicle method 
    {                    
        Console.WriteLine("Tuut, tuut!");
    }
}

class Car : Vehicle  // derived class (child)
{
    public string modelName = "Mustang";  // Car field
}

多重继承指的是一个类别可以同时从多于一个父类继承行为与特征的功能。与单一继承相对,单一继承指一个类别只可以继承自一个父类。

C# 不支持多重继承。但是可以使用接口来实现多重继承。

    class Shape
    {
        public void setWidth(int w)
        {
            width = w;
        }
        public void setHeight(int h)
        {
            height = h;
        }
        protected int width;
        protected int height;
    }

    // 基类 PaintCost
    public interface PaintCost
    {
        int getCost(int area);

    }
    // 派生类
    class Rectangle : Shape, PaintCost
    {
        public int getArea()
        {
            return (width * height);
        }
        public int getCost(int area)
        {
            return area * 70;
        }
    }

多态 Polymorphism

多态是同一个行为具有多个不同表现形式或形态的能力。

The word polymorphism means having many forms.

多态性意味着有多重形式。在面向对象编程范式中,多态性往往表现为”一个接口,多个功能”。

In object-oriented programming paradigm, polymorphism is often expressed as ‘one interface, multiple functions’

多态性可以是静态的或动态的。在静态多态性中,函数的响应是在编译时发生的。在动态多态性中,函数的响应是在运行时发生的。

Polymorphism can be static or dynamic. In static polymorphism, the response to a function is determined at the compile time. In dynamic polymorphism, it is decided at run-time.

在 C# 中,每个类型都是多态的,因为包括用户定义类型在内的所有类型都继承自 Object。

多态就是同一个接口,使用不同的实例而执行不同操作。

Polymorphism and Overriding Methods

Polymorphism means “many forms”, and it occurs when we have many classes that are related to each other by inheritance.

Like we specified in the previous chapter; Inheritance lets us inherit fields and methods from another class. Polymorphism uses those methods to perform different tasks. This allows us to perform a single action in different ways.

For example, think of a base class called Animal that has a method called animalSound(). Derived classes of Animals could be Pigs, Cats, Dogs, Birds - And they also have their own implementation of an animal sound (the pig oinks, and the cat meows, etc.):

静态多态性 Static Polymorphism

在编译时,函数和对象的连接机制被称为早期绑定,也被称为静态绑定。C# 提供了两种技术来实现静态多态性。分别为:

The mechanism of linking a function with an object during compile time is called early binding. It is also called static binding. C# provides two techniques to implement static polymorphism. They are −

动态多态性是通过 抽象类 和 虚方法 实现的。

C# allows you to create abstract classes that are used to provide partial class implementation of an interface. Implementation is completed when a derived class inherits from it.

使用关键字 abstract 创建抽象类,用于提供接口的部分类的实现。当一个派生类继承自该抽象类时,实现即完成。抽象类包含抽象方法,抽象方法可被派生类实现。派生类具有更专业的功能。

public abstract class BaseClass
{
    public abstract void AbstractMethod(); 

Alert:

Here are the rules about abstract classes −

下面是有关抽象类的一些规则:

虚方法

当有一个定义在类中的函数需要在继承类中实现时,可以使用虚方法;使用关键字 virtual 声明的。虚方法可以在不同的继承类中有不同的实现。

虚方法和抽象方法的区别:

抽象方法是只有方法名称,没有方法体(具体实现),子类必须重写父类抽象方法;

虚函数是该方法有方法体,但是子类可以覆盖,也可不覆盖。(子类根据自己的需要决定是否重写该方法,而不是必须重写)

接口 Interface

An interface is defined as a syntactical contract that all the classes inheriting the interface should follow. The interface defines the ‘what’ part of the syntactical contract and the deriving classes define the ‘how’ part of the syntactical contract.

接口定义了属性、方法和事件,这些都是接口的成员。接口只包含了成员的声明。成员的定义是派生类的责任。接口提供了派生类应遵循的标准结构。

Interfaces define properties, methods, and events, which are the members of the interface. Interfaces contain only the declaration of the members. It is the responsibility of the deriving class to define the members. It often helps in providing a standard structure that the deriving classes would follow.

interface IMyInterface
{
    void MethodToImplement();
}

Abstract classes to some extent serve the same purpose, however, they are mostly used when only few methods are to be declared by the base class and the deriving class implements the functionalities.

在类中也有抽象类的定义,抽象类与接口的区别在于:

抽象类是一个不完全的类,类里面有抽象的方法,属性,也可以有具体的方法和属性,需要进一步的专业化。

但接口是一个行为的规范,里面的所有东西都是抽象的! 一个类只可以继承一个基类也就是父类,但可以实现多个接口。

.它们的派生类只能继承一个基类,即只能继承一个抽象类,但是可以继承多个接口。

·抽象类中可以定义成员的实现,但接口中不可以。

·抽象类中包含字段、构造函数、析构函数、静态成员或常量等,接口中不可以。

·抽象类中的成员可以私有的(只要不是抽象的)、受保护的、内部的或受保护的内部成员,但接口中的成员必须是公共的。

重载运算符

重载运算符是具有特殊名称的函数,是通过关键字 operator 后跟运算符的符号来定义的。

e.g.

public static Box operator+ (Box b, Box c)
{
Box box = new Box();
box.length = b.length + c.length;
box.breadth = b.breadth + c.breadth;
box.height = b.height + c.height;
return box;
}

运算符只能采用值参数,不能采用 ref 或 out 参数。

C# 要求成对重载比较运算符。如果重载了==,则也必须重载!=,否则产生编译错误。同时,比较运算符必须返回bool类型的值,这是与其他算术运算符的根本区别。

C# 不允许重载=运算符,但如果重载例如+运算符,编译器会自动使用+运算符的重载来执行+=运算符的操作。

运算符重载的其实就是函数重载。首先通过指定的运算表达式调用对应的运算符函数,然后再将运算对象转化为运算符函数的实参,接着根据实参的类型来确定需要调用的函数的重载,这个过程是由编译器完成。

Namesapce

命名空间的设计目的是提供一种让一组名称与其他名称分隔开的方式。在一个命名空间中声明的类的名称与另一个命名空间中声明的相同的类的名称不冲突。

预处理器指令

#define	它用于定义一系列成为符号的字符。
#undef	它用于取消定义符号。
#if	它用于测试符号是否为真。
#else	它用于创建复合条件指令,与 #if 一起使用。
#elif	它用于创建复合条件指令。
#endif	指定一个条件指令的结束。
#line	它可以让您修改编译器的行数以及(可选地)输出错误和警告的文件名。
#error	它允许从代码的指定位置生成一个错误。
#warning	它允许从代码的指定位置生成一级警告。
#region	它可以让您在使用 Visual Studio Code Editor 的大纲特性时,指定一个可展开或折叠的代码块。
#endregion	它标识着 #region 块的结束。

总的来说和普通的控制语句(if等)功能类似,方便在于预处理器指令包含的未执行部分是不需要编译的。


C# 常用 数据结构和库函数

初始化二维数组

int [,] a = new int [3,4] {
{0, 1, 2, 3} ,   /*  初始化索引号为 0 的行 */
{4, 5, 6, 7} ,   /*  初始化索引号为 1 的行 */
{8, 9, 10, 11}   /*  初始化索引号为 2 的行 */
};

// 3维
int [ , , ] m;

数组中是否存在元素

// Method 1
string[] s1 = new string[3] { "John", "Paul", "Mary" };
if (s1.Contains("John"))
    // Exist
 
// Method 2
int id = Array.IndexOf(s1, value);
 
if(id == -1)
    // Not Exist
else
    // Exist

List

Property

.Count = (Length) Elem amopunts

.Items Gets or sets the element at the specified index

Methods

Method Usage
Add Adds an element at the end of a List.
AddRange Adds elements of the specified collection at the end of a List.
BinarySearch Search the element and returns an index of the element.
Clear Removes all the elements from a List.
Contains Checks whether the specified element exists or not in a List.
Find Finds the first element based on the specified predicate function.
Foreach Iterates through a List.
Insert Inserts an element at the specified index in a List.
InsertRange Inserts elements of another collection at the specified index.
Remove Removes the first occurrence of the specified element.
RemoveAt Removes the element at the specified index.
RemoveRange Removes all the elements that match the supplied predicate function.
Sort Sorts all the elements.
TrimExcess Sets the capacity to the actual number of elements.
TrueForAll Determines whether every element in the List matches the conditions defined by the specified predicate.

Insert

var numbers = new List<int>(){ 10, 20, 30, 40 };

numbers.Insert(1, 11);// inserts 11 at 1st index: after 10.

foreach (var num in numbers)
    Console.Write(num);

Remove

var numbers = new List<int>() { 10, 20, 30, 40, 10 };

numbers.Remove(10); // removes the first 10 from a list

numbers.RemoveAt(2); //removes the 3rd element (index starts from 0)


foreach (var el in numbers)
    Console.Write(el); //prints 20 30 10

Sort

list.Sort((a,b) => a.property.CompareTo(b.property))

// For a complicated rule -> implement IComparable
// e.g.
public class cTag:IComparable<cTag> {
public int id { get; set; }
public int regnumber { get; set; }
public DateTime date { get; set; }
public int CompareTo(cTag other) {
    return date.CompareTo(other.date);
} }

String 相关

    // 字符串,字符串 + 连接
    string fname, lname;
    fname = "Rowan";
    lname = "Atkinson";
    string fullname = fname + lname;
    Console.WriteLine("Full Name: {0}", fullname);

    // 通过使用 string 构造函数 以字符数组char[]实例化 string
    char[] letters = { 'H', 'e', 'l', 'l','o' };
    string greetings = new string(letters);
    Console.WriteLine("Greetings: {0}", greetings);

    // String.Join()方法返回字符串
    string[] sarray = { "Hello", "From", "Tutorials", "Point" };
    string message = String.Join(" ", sarray);
    Console.WriteLine("Message: {0}", message);

    // 比较字符串
    string str1 = "This is test";
    string str2 = "This is text";
    if (String.Compare(str1, str2) == 0) // false
    {
        Console.WriteLine(str1 + " and " + str2 +  " are equal.");
    }

    // 字符串包含字符串
    string str = "This is test";
    if (str.Contains("test"))
    {
        Console.WriteLine("The sequence 'test' was found.");
    }

    // 子串
    string str = "A long string";
    string substr = str.Substring(2); // "long string"
    string substr = str.Substring(2, 3); // "lon" , second para = Length

    // 连接字符串
    string[] starray = new string[]{"Down the way nights are dark",
         "And the sun shines daily on the mountain top",
         "I took a trip on a sailing ship",
         "And when I reached Jamaica",
         "I made a stop"};

    string str = String.Join("\n", starray);
    /*  RESULT */
    /*  Down the way nights are dark
        And the sun shines daily on the mountain top
        I took a trip on a sailing ship
        And when I reached Jamaica
        I made a stop
    */

    // 格式化(e.g. 保留6位小数)
    string ansPos = ((float)posCount / arr.Count).ToString("0.000000");

    // Split 方法
    string[] time = s.Split(' ');

    // String to int
    Convert.ToInt32("-12345") // -12345

C# - SortedList<TKey, TValue>

The SortedList<TKey, TValue>, and SortedList are collection classes that can store key-value pairs that are sorted by the keys based on the associated IComparer implementation. For example, if the keys are of primitive types, then sorted in ascending order of keys.

SortedList<int,string> numberNames = new SortedList<int,string>()
                                    {
                                        {3, "Three"},
                                        {5, "Five"},
                                        {1, "One"}
                                    };

Console.WriteLine("---Initial key-values--");

foreach(KeyValuePair<int, string> kvp in numberNames)
    Console.WriteLine("key: {0}, value: {1}", kvp.Key , kvp.Value );

numberNames.Add(6, "Six");
numberNames.Add(2, "Two");
numberNames.Add(4, "Four");

Dictionary

IDictionary<int, string> numberNames = new Dictionary<int, string>();
numberNames.Add(1,"One"); //adding a key/value using the Add() method
numberNames.Add(2,"Two");
numberNames.Add(3,"Three");

//The following throws run-time exception: key already added.
//numberNames.Add(3, "Three"); 

foreach(KeyValuePair<int, string> kvp in numberNames)
    Console.WriteLine("Key: {0}, Value: {1}", kvp.Key, kvp.Value);
		
//creating a dictionary using collection-initializer syntax
var cities = new Dictionary<string, string>(){
	{"UK", "London, Manchester, Birmingham"},
	{"USA", "Chicago, New York, Washington"},
	{"India", "Mumbai, New Delhi, Pune"}
};

Advanced

特性 Attribute

特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的。

An attribute is a declarative tag that is used to convey information to runtime about the behaviors of various elements like classes, methods, structures, enumerators, assemblies etc. in your program. You can add declarative information to a program by using an attribute. A declarative tag is depicted by square ([ ]) brackets placed above the element it is used for.

特性(Attribute)用于添加元数据,如编译器指令和注释、描述、方法、类等其他信息。.Net 框架提供了两种类型的特性:预定义特性和自定义特性。

Attributes are used for adding metadata, such as compiler instruction and other information such as comments, description, methods and classes to a program. The .Net Framework provides two types of attributes: the pre-defined attributes and custom built attributes.

预定义特性(Attribute)

.Net 框架提供了三种预定义特性:

创建自定义特性(Attribute) .Net 框架允许创建自定义特性,用于存储声明性的信息,且可在运行时被检索。该信息根据设计标准和应用程序需要,可与任何目标元素相关。

创建并使用自定义特性包含四个步骤:

声明自定义特性 构建自定义特性 在目标程序元素上应用自定义特性 通过反射访问特性

反射 Reflection

Reflection objects are used for obtaining type information at runtime.

反射指程序可以访问、检测和修改它本身状态或行为的一种能力。

The classes that give access to the metadata of a running program are in the System.Reflection namespace.

The System.Reflection namespace contains classes that allow you to obtain information about the application and to dynamically add types, values, and objects to the application.

反射(Reflection)有下列用途:

Reflection has the following applications −

优缺点

优点:

1、反射提高了程序的灵活性和扩展性。

2、降低耦合性,提高自适应能力。

3、它允许程序创建和控制任何类的对象,无需提前硬编码目标类。

缺点:

1、性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和拓展性要求很高的系统框架上,普通程序不建议使用。

2、使用反射会模糊程序内部逻辑;程序员希望在源代码中看到程序的逻辑,反射却绕过了源代码的技术,因而会带来维护的问题,反射代码比相应的直接代码更复杂。

属性 Properties

属性(Property) 是类(class)、结构(structure)和接口(interface)的命名(named)成员。类或结构中的成员变量或方法称为 域(Field)。属性(Property)是域(Field)的扩展,且可使用相同的语法来访问。它们使用 访问器(accessors) 让私有域的值可被读写或操作。

属性(Property)不会确定存储位置。相反,它们具有可读写或计算它们值的 访问器(accessors)。

例如,有一个名为 Student 的类,带有 age、name 和 code 的私有域。我们不能在类的范围以外直接访问这些域,但是我们可以拥有访问这些私有域的属性。

访问器(Accessors) 属性(Property)的访问器(accessor)包含有助于获取(读取或计算)或设置(写入)属性的可执行语句。访问器(accessor)声明可包含一个 get 访问器、一个 set 访问器,或者同时包含二者

索引器 Indexer

索引器(Indexer) 允许一个对象可以像数组一样使用下标的方式来访问。

当您为类定义一个索引器时,该类的行为就会像一个 虚拟数组(virtual array) 一样。您可以使用数组访问运算符 [ ] 来访问该类的的成员。

An indexer allows an object to be indexed such as an array. When you define an indexer for a class, this class behaves similar to a virtual array. You can then access the instance of this class using the array access operator ([ ]).

重载索引器(Indexer)

索引器(Indexer)可被重载。索引器声明的时候也可带有多个参数,且每个参数可以是不同的类型。没有必要让索引器必须是整型的。C# 允许索引器可以是其他类型,例如,字符串类型。

委托 Delegate

C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针。委托(Delegate) 是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。

C# delegates are similar to pointers to functions, in C or C++. A delegate is a reference type variable that holds the reference to a method. The reference can be changed at runtime.

Delegates are especially used for implementing events and the call-back methods. All delegates are implicitly derived from the System.Delegate class.

委托(Delegate)特别用于实现事件和回调方法。所有的委托(Delegate)都派生自 System.Delegate 类。

委托的多播(Multicasting of a Delegate)

委托对象可使用 “+” 运算符进行合并。一个合并委托调用它所合并的两个委托。只有相同类型的委托可被合并。”-“ 运算符可用于从合并的委托中移除组件委托。

使用委托的这个有用的特点,您可以创建一个委托被调用时要调用的方法的调用列表。这被称为委托的 多播(multicasting),也叫组播。

事件 Event

事件(Event) 基本上说是一个用户操作,如按键、点击、鼠标移动等等,或者是一些提示信息,如系统生成的通知。应用程序需要在事件发生时响应事件。例如,中断。

Events are user actions such as key press, clicks, mouse movements, etc., or some occurrence such as system generated notifications. Applications need to respond to events when they occur. For example, interrupts.

C# 中使用事件机制实现线程间的通信。

Events are used for inter-process communication.

通过事件使用委托 事件在类中声明且生成,且通过使用同一个类或其他类中的委托与事件处理程序关联。包含事件的类用于发布事件。这被称为 发布器(publisher) 类。其他接受该事件的类被称为 订阅器(subscriber) 类。事件使用 发布-订阅(publisher-subscriber) 模型。

发布器(publisher) 是一个包含事件和委托定义的对象。事件和委托之间的联系也定义在这个对象中。发布器(publisher)类的对象调用这个事件,并通知其他的对象。

订阅器(subscriber) 是一个接受事件并提供事件处理程序的对象。在发布器(publisher)类中的委托调用订阅器(subscriber)类中的方法(事件处理程序)。

声明事件(Event) 在类的内部声明事件,首先必须声明该事件的委托类型。例如:

public delegate void BoilerLogHandler(string status);

然后,声明事件本身,使用 event 关键字:

// 基于上面的委托定义事件
public event BoilerLogHandler BoilerEventLog;

泛型Generic

泛型(Generic) 允许您延迟编写类或方法中的编程元素的数据类型的规范,直到实际在程序中使用它的时候。换句话说,泛型允许您编写一个可以对任何数据类型都能用的类或方法。

Generics allow you to define the specification of the data type of programming elements in a class or a method, until it is actually used in the program. In other words, generics allow you to write a class or method that can work with any data type.

您可以通过数据类型的替代参数编写类或方法的规范。当编译器遇到类的构造函数或方法的函数调用时,它会生成代码来处理指定的数据类型。

You write the specifications for the class or the method, with substitute parameters for data types. When the compiler encounters a constructor for the class or a function call for the method, it generates code to handle the specific data type.

泛型(Generic)的特性

使用泛型是一种增强程序功能的技术,具体表现在以下几个方面:

它有助于您最大限度地重用代码、保护类型的安全以及提高性能。 您可以创建泛型集合类。.NET 框架类库在 System.Collections.Generic 命名空间中包含了一些新的泛型集合类。您可以使用这些泛型集合类来替代 System.Collections 中的集合类。 您可以创建自己的泛型接口、泛型类、泛型方法、泛型事件和泛型委托。 您可以对泛型类进行约束以访问特定数据类型的方法。 关于泛型数据类型中使用的类型的信息可在运行时通过使用反射获取。

Features of Generics

Generics is a technique that enriches your programs in the following ways −

It helps you to maximize code reuse, type safety, and performance.

You can create generic collection classes. The .NET Framework class library contains several new generic collection classes in the System.Collections.Generic namespace. You may use these generic collection classes instead of the collection classes in the System.Collections namespace.

You can create your own generic interfaces, classes, methods, events, and delegates.

You may create generic classes constrained to enable access to methods on particular data types.

You may get information on the types used in a generic data type at run-time by means of reflection.

匿名方法

我们已经提到过,委托是用于引用与其具有相同标签的方法。换句话说,您可以使用委托对象调用可由委托引用的方法。

We discussed that delegates are used to reference any methods that has the same signature as that of the delegate. In other words, you can call a method that can be referenced by a delegate using that delegate object.

匿名方法(Anonymous methods) 提供了一种传递代码块作为委托参数的技术。匿名方法是没有名称只有主体的方法。

Anonymous methods provide a technique to pass a code block as a delegate parameter. Anonymous methods are the methods without a name, just the body.

在匿名方法中您不需要指定返回类型,它是从方法主体内的 return 语句推断的。

You need not specify the return type in an anonymous method; it is inferred from the return statement inside the method body.

编写匿名方法的语法 匿名方法是通过使用 delegate 关键字创建委托实例来声明的。例如:

delegate void NumberChanger(int n);
...
NumberChanger nc = delegate(int x)
{
    Console.WriteLine("Anonymous Method: {0}", x);
};

代码块 Console.WriteLine(“Anonymous Method: {0}”, x); 是匿名方法的主体。

委托可以通过匿名方法调用,也可以通过命名方法调用,即,通过向委托对象传递方法参数。

注意: 匿名方法的主体后面需要一个 ;。

不安全代码 Unsafe Codes

当一个代码块使用 unsafe 修饰符标记时,C# 允许在函数中使用指针变量。不安全代码或非托管代码是指使用了指针变量的代码块。

C# allows using pointer variables in a function of code block when it is marked by the unsafe modifier. The unsafe code or the unmanaged code is a code block that uses a pointer variable.

Interview Questions

OOP General

Class is a template for objects, and an object is an instance of a class.

Inheritance in OOP = When a class derives from another class. The child class will inherit all the public and protected properties and methods from the parent class. In addition, it can have its own properties and methods.

C# 封装根据具体的需要,设置使用者的访问权限,并通过 访问修饰符 来实现。 C# encapsulation sets the user’s access rights according to specific needs, and implements it through access specifier.

Does OOP support multiple inheritance? [6]

It depends. There is no requirement in OO to support multiple inheritance, which is supported by languages such as C++. C# and Java don’t support, but they can implement multiple inheritance by interface.

Why do we use multiple inheritance?

Multiple inheritance is useful when a subclass needs to combine multiple contracts and inherit some, or all, of the implementation of those contracts. For example, the AmericanStudent class needs to inherit from both the Student class and the American class.

Why multiple inheritance is not there in Java? [7]

The reason behind this is to prevent ambiguity. Consider a case where class B extends class A and Class C and both class A and C have the same method display(). Now java compiler cannot decide, which display method it should inherit. To prevent such situation, multiple inheritances is not allowed in java.

It was a conscious design decision to avoid the problems of working out precedence when there are clashes caused by inheriting from two sets of classes with clashing implementations of method signatures.

Instead, interfaces were introduced as a lightweight way of defining multiple signatures for classes without ever having to resolve conflicting method bodies. Just collapse the signatures into one signature. Neat.

What is Three Tier Architecture? [9]

Three-tier models include an application logic between the client and the server, which handles the data processing and allows a certain degree of interaction. For example, an application server can process data while a database server is dedicated solely to data storage. In this way, content can be dynamically loaded and saved. The script language JavaScript is often responsible for the behavior of the client.

p.s. Client-server model

Initially, the web consisted of a two-tiered architecture: clients and servers. Clients and servers shared the tasks and services that the system was supposed to perform. For example, the client may request a service from the server; the server answers the request by providing the service. Retrieving a website using a URL address that directs to a server to load the site in the client’s browser is an example of the two-layer model, also known as the client-server model.

Generally, a distinction is made between server-side and client-side data processing. Dynamic websites are characterized by the fact that content is changed on the client side without new communication between server and client being required. Action on the client side is influenced by scripts so that no asynchronous data transfer is necessary. On the server side, modified content is stored via the application server on the database server. Optionally, this can be a virtual server that emulates a physical one.

What are the Benefits of Three Tier Architecture? [10]

Reusability: You can reuse the middle layer with different user interfaces like ASP.NET, windows etc. You can also reuse your DAL(Data Access Layer) with different projects.

Maintainability: When you change in one layer due to the modular approach it does not have ripple effect on other layers. You have to do less amount of changes in other layer when you change logic of one layer.

Reference

[1] Runoob C# - https://www.runoob.com/csharp/csharp-intro.html

[2] C#和.NET的关系和区别 - https://www.cnblogs.com/yangxuming/p/8430803.html

[3] C#与.NET之间的关系 - https://blog.csdn.net/weixin_44543431/article/details/97231772

[4] out parameter modifier (C# Reference) - https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/out-parameter-modifier

[5] C# OOP - https://www.w3schools.com/cs/cs_oop.php

[6] https://lynniezulu.com/does-oop-support-multiple-inheritance/

[7] Why is there multiple inheritance in C++ but not in Java? - https://www.quora.com/Why-is-there-multiple-inheritance-in-C++-but-not-in-Java?share=1

[8] TutorialsPoint C# - https://www.tutorialspoint.com/csharp/csharp_polymorphism.htm

[9] Web Architecture - https://en.ryte.com/wiki/Web_Architecture

[10] OOPS Interview Questions - C# - https://www.c-sharpcorner.com/article/oops-interview-questions-c-sharp/

Comments