C#基础入门

在CLR环境中运行程序,需要提前进行编译,编译分为两个阶段:

​ 1. 将源代码编译为中间语言(MSIL, Microsoft Intermediate Language).

​ 2. 将MSIL编译为平台专用的代码.

\^1\]: Common Language Infrastructure.通用语言基础结构,CLI是CLR的一部分。 在CLR结构图中,CLI位于下半部分,主要包含类加载器(Class Loader)、实时编译器(IL To Native Compilers)和一个运行时环境的垃圾收集器(Garbage Collector)。CLI是.Net和CLR的灵魂,CLI为IL代码提供运行的环境,你可以将使用任何语言编写的代码通过特定的编译器转换为MSIL代码后运行其上,甚至还可以自己编写MSIL代码在CLI上面运行。 \[\^2\]: Common Language Runtime.通用语言运行库,是.Net Framework核心的执行环境。通常将在CLR控制下运行的代码称为托管代码。 ## 中间语言IL(Intermediate Language) 1. 值类型与引用类型 中间语言提供了许多预定义的基本数据类型,它的一个特性是值类型与引用类型之间有明显的区别。 引用类型:变量仅存储地址(4或8字节),真正的实例存储在名为"托管堆"的内存区域中。 值类型:变量直接存储其数据,通常直接存放在栈上。(如果作为引用类型的成员,则作为内联也存放在托管堆上)。 2. 通用类型系统 中间语言要在本层提供语言之间的互操作性,必须有一套通用的数据类型系统(CTS)。CTS定义了可以在中间语言中使用的预定义数据类型,所有面向.Net Framework的语言都可以生成最终基于这些类型的编译代码。CTS指定了基本数据(int,float,double...)的值类型,提供了两个引用类型:object 根类型和string字符串类型,此外还约定了一个内容丰富的类型层次结构,允许不同的语言中自定义数据类型。 3. 垃圾回收 在托管堆上的创建的数据对象,无需手动释放。CLR会不时的调用垃圾回收期进行托管堆的维护,这个过程是不确定的,不能保证什么引用计数为0时,对象资源就会被回收。 ## C#面向对象基础 1. 构造函数。如果设计了一个类,没有提供任何形式的构造函数,则编译器会在编译时生成一个无参数的基本构造函数,将所有的成员字段初始化为标准的默认值:引用类型字段指向null空引用,值类型字段设为全0。如果设计类时,提供了带参数的构造函数,编译器将不会生成其他构造函数,此时就要求类的设计者自行对各个成员字段完成初始化。在客户端使用这个类时,也无法用无参的构造函数实例化该类。 2. 析构函数。由于C#托管机制的存在,上层用户不必考虑对象析构的问题,因此虽然C#允许为类定义析构函数(语法与C++一样,波浪号+类名,无参),但一般很少在C#代码里看到析构函数,如果有析构函数,则在编译阶段将被自动转换为Finalize重载。 3. 如果在定义类时没有指定基类,编译器将假定该类直接继承Object类(结构体默认继承ValueType类,而ValueType继承Object类,但是结构体之间不能存在继承关系,结构体定义的无参构造函数由编译器生成,不允许覆盖重写)。Object基类的方法: - ToString方法:获取对象的字符串表示,返回字符串的引用。如果子类没有重写该方法,则返回类的名称。 - GetHashCode()方法:如果希望把类用作字典的键,则需要重写该方法。 - Equals()方法:判断两个对象值是否相等 - Finalize()方法:引用类型对象在被GC垃圾回收时,由回收器发起调用,可以看作是C#类的通用析构函数,用于执行一些非托管资源的清理工作。Object基类的该方法内部为空。因为垃圾回收器不能处理非托管资源(譬如打开的文件句柄, Windows内核对象 ,事件,未断开的网络连接等),所以存在非托管资源的类,可以重载Finalize()方法,在里面执行非托管资源释放。然而,又由于垃圾回收是一个低优先级任务,Finalize的调用可能不那么及时,如果短时间内出现大量非托管资源申请(比如网络套接字申请),则会导致资源耗尽。所以拥有非托管资源的类,需要自行定义资源释放方法,客户端创建的实例在使用结束后,需要显示调用该方法,完成资源释放。为了规范化这个过程,C#提供了IDisposable接口类,声明了Dispose()方法。拥有非托管资源的类,可以继承该接口,按规范实现Dispose。 - GetType()方法:该方法返回一个System.Type派生类的实例,该实例包含了对象各个成员的详细信息。C#中的反射机制依赖该接口实现。 - MemberwiseClone()方法:复制对象生成副本,并返回副本的一个引用。需要注意的是,副本是一个浅复制,所有的值类型成员拷贝了一份,剩余的引用类型成员,则只是复制了引用,而不是复制引用的对象。该方法不是虚方法,所以不能重写它的实现。 3. 多重继承 C#不支持多重继承,除非多出来的是接口类。结构体不允许继承,但是,允许有多个接口基类。 4. 存在继承关系时,对象的构造顺序是先基类,再派生类。派生类的构造函数后面,通过初始化列表的形式,发起基类构造函数的调用,与C++不同的,C#通过关键字base进行调用而不是基类名。如果基类只有默认的无参构造函数,则可以省略base()的调用,编译器在编译时会自动添加。 \`\`\`c# public class SubClassA:A { public SubClassA():base(xxx){} //For c++, there will be "SubClassA():A(xxx){}" } \`\`\` 5. 接口类中只允许存在方法,属性(注意是属性,不是字段)的声明,而不允许提供实现,且不能声明为静态、私有和虚方法,也不能有构造函数。继承接口的类,需要定义接口中声明的方法和属性,如果没全部定义,它就是抽象类,不允许构造实例。 \`\`\`c# //1.Microsoft 预定义的一个接口System.IDisposable public interface IDisposable { void Dispose(); }; //2.自定义接口 public interface ISomeInterface { void Func1(int i); bool Func2(decimal amount); string Name{ get; set;}//属性声明 }; \`\`\` 6. 属性和字段 属性是对字段的封装,用于提供对字段的安全读写保护。当只定义了属性,没有显式规定该属性所保护的字段时,编译器将创建一个匿名字段。 class Person { public string Name;//不受保护的字段 private string m_age;//受保护的字段 public string Age { get { return m_age; } private set { m_age = value; } }//属性定义 //public string Age=\>m_age;//等效写法 }

7.结构体为值类型,一般存在于栈上。但是如果作为类的成员(包括静态成员),或在封箱操作时,会自动变成引用类型,变成托管变量(结构体如果继承了接口类,则它可以封箱成接口类,也可以从接口类拆箱成相应的值类型,否则他只能和Object类型进行拆封箱操作)。普通结构体类型在作为函数形参时,默认是值传递,如果加了ref /in/out,则会成为引用,注意这种引用方式不是通过封箱来实现的。如果结构体定义时添加了ref,则该结构体不能用作类的字段,不允许它有封箱操作,不能继承接口类,只能作为值类型使用,在函数传参时无论加不加ref都以引用方式传递。(封箱指的是一种值类型转引用类型的操作,封箱会导致在托管堆上多出一个副本,得到的引用是引用托管堆上这个副本,因此基于引用变量的更新操作,不会影响原对象)。

```c#

public ref struct MyRefStruct

{

public int MyIntValue1;

public int MyIntValue2;

}

```

  1. C#的事件和委托

1>. C#的委托是一种引用类型,包含了一个或多个函数地址以及这些函数的细节信息,可以看作是C++的函数指针类型的扩展。

2>. 事件是对委托的封装,委托作为事件的私有成员,由事件对外提供受约束的访问。委托可以独立存在,而事件脱离了委托就没有意义。

以下代码片段等价(编译器在底层将第一种写法自动转换为第二种写法):

'''

using System;

namespace CSharpConsoleApplication2

{

public class EventTest

{

public delegate void StateChangeHandler();//声明委托类型(类似声明函数指针)

public event StateChangeHandler StatechangeEvent;//定义基于该类型委托的事件

public void OnStateChange()//触发

{

StatechangeEvent?.Invoke();//StatechangeEvent的invoke方法为私有,外部无法直接调用

}

}

public class Program

{

public static void Method()

{

Console.WriteLine("Event Triggered.");

}

public static void Main(string[] args)

{

EventTest eventTest = new EventTest();

eventTest.StatechangeEvent += Method;//注册回调

eventTest.OnStateChange();//触发事件

Console.WriteLine("End.");

}

}

}

'''

等价于:

'''

using System;

namespace CSharpConsoleApplication2

{

public class EventTest

{

public delegate void StateChangeHandler();

private StateChangeHandler _stateChangeHandler;

public event StateChangeHandler StatechangeEvent

{

add { _stateChangeHandler += value; }

remove { _stateChangeHandler -= value; }

}

public void OnStateChange()

{

_stateChangeHandler?.Invoke();

}

}

public class Program

{

public static void Method()

{

Console.WriteLine("Event Triggered.");

}

public static void Main(string[] args)

{

EventTest eventTest = new EventTest();

eventTest.StatechangeEvent += Method;

eventTest.OnStateChange();

Console.WriteLine("End.");

}

}

}

'''

  1. Task

C#的Task类实现了awaitable pattern 等待模式,能够配合await/sync实现异步编程。下面仿照实现一个awaitable的Task类,帮助理解异步编程原理

cs 复制代码
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

namespace AsyncAwaitDemo
{
    class Program
    {
        static async System.Threading.Tasks.Task Test()
        {
            var t = new MyCpuTask();
            await t;
            //遇到await关键字,c#关键字会为其编译成以下等效代码(伪代码)
            /*var awaiter = t.GetAwaiter();
            if(!awaiter.IsCompleted) 
            {
                awaiter.OnCompleted(stateMachine.Resume);//stateMachine是编译器创建的状态机,保存了当前调用上下文,此处不阻塞
                return;
            }
            else
                return;
            */
            // 如果在await的对象是带返回值的类型,编译器将生成更为复杂的结构,用于存放返回值
        }
        static async System.Threading.Tasks.Task<string> TestRtn()//带返回值的Task
        {
            Func<string> func = () =>
            {
                Console.WriteLine("Do task Begin (string)...");
                Thread.Sleep(5000);
                Console.WriteLine("Do task End (string)...");
                return "Hello Awaitable";
            };
            var t = MyCpuTaskRtn<string>.Run(func);
            var res = await t;//编译器将在此生成一系列代码
            return res;
        }
        
        static void Main(string[] args)
        {
            var res1 = Test();
            var waiter1 = res1.GetAwaiter();
            waiter1.GetResult();

            var res2 = TestRtn();
            var waiter2 = res2.GetAwaiter();
            var str = waiter2.GetResult();

            Console.WriteLine("Res : {0}, Any Key to Exit...", str);
            Console.ReadKey();
        }
    }
    public class MyCpuTask
    {
        public delegate void TaskHandlerDelegate();
        private TaskHandlerDelegate m_TaskHandler;
        public class MyAwaiter : INotifyCompletion
        {
            private Exception m_error;
            private Action m_continuation;
            private TaskHandlerDelegate m_task;
            private volatile bool m_completed;
            public bool IsCompleted{get{return m_completed;} private set{m_completed = value;}}
            public MyAwaiter(TaskHandlerDelegate t)
            {
                if (t == null) throw new ArgumentNullException(nameof(t));
                m_completed = false;
                m_continuation = null;
                m_error = null;
                m_task = t;
                ThreadPool.QueueUserWorkItem(Callback);// Register the CPU task. you can also register the IO Event or timer Event.
            }
            public void GetResult()
            {
                if (!m_completed)
                    throw new InvalidOperationException("Async Operation has not completed yet.");
                if (m_error != null)
                {
                    Console.WriteLine("Async Operation Failed.");
                    ExceptionDispatchInfo.Capture(m_error).Throw();
                }
                Console.WriteLine("Async Operation Finished, Fetching Result Now...");
            }
            public void OnCompleted(Action continuation)
            {
                if (continuation == null)
                    throw new ArgumentNullException(nameof(continuation));
                if (Interlocked.CompareExchange(ref m_continuation, continuation, null) != null)
                    throw new InvalidOperationException("This awaiter does not support multiple awaits.");
                if (m_completed)//If completed, trigger the subsequent actions directly.
                { 
                    var cont = Interlocked.Exchange(ref m_continuation, null);
                    if (cont != null)
                        ThreadPool.QueueUserWorkItem(_=> { cont.Invoke(); });
                }
            }

            private void Callback(object state)
            {
                try
                {
                    if (m_task != null)
                        m_task.Invoke();//Do task;
                    m_completed = true;
                    var cont = Interlocked.Exchange(ref m_continuation, null);
                    try {
                        if (cont != null)
                        {
                            cont();// Reume the subsequent actions. 
                            //if(SynchronizationContext.Current != null)//在UI 应用中, Current不为空, 可以选择将continuation放到UI线程执行.
                            //SynchronizationContext.Current.Post(_=>cont(), state);
                        }
                    }
                    catch (Exception ex)//Reume Failed.
                    {
                        m_error = ex;
                    }
                }
                catch (Exception ex)//Task Failed.
                {
                    m_error = ex;
                }
                finally
                {
                    m_completed = true;
                }
            }
        }

        public MyCpuTask()
        {
            m_TaskHandler = null;
        }
        public MyAwaiter GetAwaiter()
        {
            m_TaskHandler = () => { Console.WriteLine("Do task Begin..."); Thread.Sleep(2000); Console.WriteLine("Do task End..."); };
            return new MyAwaiter(m_TaskHandler);
        }
    }

    public class MyCpuTaskRtn<TResult>
    {
        public delegate TResult TaskHandlerDelegate();
        private readonly TaskHandlerDelegate m_TaskHandler;

        public class MyAwaiter : INotifyCompletion
        {
            private Exception m_error;
            private Action m_continuation;
            private readonly TaskHandlerDelegate m_task;
            private volatile bool m_completed;
            private TResult m_result;

            public bool IsCompleted => m_completed;

            public MyAwaiter(TaskHandlerDelegate t)
            {
                if (t == null) throw new ArgumentNullException(nameof(t));
                m_task = t;
                m_completed = false;
                m_continuation = null;
                m_error = null;

                ThreadPool.QueueUserWorkItem(Callback);
            }

            public TResult GetResult()
            {
                if (!m_completed)
                    throw new InvalidOperationException("Async Operation has not completed yet.");

                if (m_error != null)
                {
                    Console.WriteLine("Async Operation Failed.");
                    ExceptionDispatchInfo.Capture(m_error).Throw();
                }

                Console.WriteLine("Async Operation Finished, Fetching Result Now...");
                return m_result;
            }

            public void OnCompleted(Action continuation)
            {
                if (continuation == null)
                    throw new ArgumentNullException(nameof(continuation));

                if (Interlocked.CompareExchange(ref m_continuation, continuation, null) != null)
                    throw new InvalidOperationException("This awaiter does not support multiple awaits.");

                if (m_completed)
                {
                    var cont = Interlocked.Exchange(ref m_continuation, null);
                    if (cont != null)
                    {
                        ThreadPool.QueueUserWorkItem(_ => cont());
                    }
                }
            }

            private void Callback(object _)
            {
                try
                {
                    m_result = m_task.Invoke();//Do Task
                }
                catch (Exception ex)
                {
                    m_error = ex;//Do Task Failed 
                }
                finally
                {
                    m_completed = true;
                    var cont = Interlocked.Exchange(ref m_continuation, null);
                    if (cont != null)
                    {
                        try
                        {
                            cont();
                        }
                        catch (Exception ex)//Resume Failed.
                        {
                            if (m_error == null)
                                m_error = ex;
                        }
                    }
                }
            }
        }

        public MyCpuTaskRtn(TaskHandlerDelegate handler)
        {
            if (handler == null)
                throw new ArgumentNullException(nameof(handler));
            m_TaskHandler = handler;
        }

        public MyAwaiter GetAwaiter()
        {
            return new MyAwaiter(m_TaskHandler);
        }

        public static MyCpuTaskRtn<TResult> Run(Func<TResult> func)
        {
            if (func == null)
                throw new ArgumentNullException(nameof(func));
            return new MyCpuTaskRtn<TResult>(() => func());
        }
    }


}
  1. Task与async/await机制的关系

.NET 4.0就出现Task类(2010.04),将耗时操作加入线程池来进行异步化,用成员函数Wait来阻塞等待结果,如果想要在任务结束后执行其他操作(所谓链式操作),需要用成员函数ContinueWith注册一个回调来进行,如果回调内部继续用Task和ContinueWith,这样很容易在多次嵌套后,陷入回调地狱,代码难以调试。.NET 5.0 (2012)中提出了async/wait机制,并为Task扩展了上述awaitable模型,让编译器自动生成维护异步模型的代码,简化了C#编写异步程序的过程的同时,也提高了稳定性。

深入探讨 C# 和 .NET 中 async/await 的历史、背后的设计决策和实现细节 - wxlevel - 博客园

相关推荐
女王大人万岁2 小时前
Golang标准库 CGO 介绍与使用指南
服务器·开发语言·后端·golang
myzzb2 小时前
纯python 最快png转换RGB截图方案 ——deepseek
开发语言·python·学习·开源·开发
t198751282 小时前
基于Chirp分解和多相快速算法的离散分数傅里叶变换(DFRFT)MATLAB实现
开发语言·算法·matlab
jllllyuz2 小时前
基于MATLAB的PAM通信系统仿真实现
开发语言·matlab
程序员小假2 小时前
我们来说一下虚拟内存的概念、作用及实现原理
java·后端
qq_448011162 小时前
python中的内置globals()详解
开发语言·python
悠哉清闲2 小时前
Future
java·开发语言·kotlin
deepxuan3 小时前
Day2--python三大库-numpy
开发语言·python·numpy
徐同保3 小时前
python如何手动抛出异常
java·前端·python