C#基础知识0:从我个人角度说说学习C#之前必须掌握的基础知识和概述

1 概念和基础

Why Java or C#:

在当前市场上,使用到的编程语言,排名前五位的分别为:Java、C、C++、Python、C#。而在大型应用开发层面上Java和C#仍然是开发人员的首选,当然Python这两年奋起直追,但Python的设计思路和C#、Java并不相同,所以就暂是抛开Python不提了(Python粉不要拍砖,我也喜欢Python)。

Win10推出之后,开发市场上,windows平台上开发应用又回光返照了一阵子,且如果要做一些需要大量本地计算的Windows应用开发,C# + .NET仍然是一个非常不错甚至是唯一的选择。
在这里插入图片描述

按照语言运行环境分类的不同,我把这五大巨头分成了两类,如下:

  • 托管Runtime类型语言
    • Java——JRE(Java虚拟机和核心类库)
    • Python——Python解释器
    • C#——CLR(.NET Framework)
  • 非托管类型语言
    • C、C++——编译后直接执行机器语言

可以看到,五大巨头中过半的语言使用了构建在Runtime之上的设计架构,无论它是叫虚拟机也好,解释器也好,个人觉得它们的目的是以下两个

  • 平台无关性
  • 开发的便利性和安全性

多几句废话:

如果能够掌握了C#的设计思想,那么基本上也就认识了老大Java,原因就是由于C#推出的目的有很大的成分是微软用来对抗Java的,但跨平台和开源盛行的今天,微软每推出一项技术,得到的结果基本上都是叫好不叫座,C#这么多年了,不温不火,TypeScript也是同样的情况。移动平台上的Xamarin使用的人也是寥寥无几。一个语言或者框架的发展都是离不开社区的,微软应该反省。

那么如果选用C#在Windows下进行开发后,那么就需要了解C#的基本特性以及需要明确一个学习路径,有非常多的C#入门文章,一上来就去扯什么值类型、引用类型,装箱、拆箱等这些偏于底层的基础概念,浪费了新手大量的入门时间。所以我觉得,这些知识在使用一段时间C#之后,再去熟悉,即能够深刻理解时机上也不晚。

PS.这篇文章编写的前提是要求读者至少了解面向对象思想,使用过一段时间C++ 是最好的,如果不了解C++的话,最好先找一本基础读物了解一下,个人推荐《大话设计模式》这本书的附录部分,它对C#的基础知识做了非常直白的描述。

1.1 了解C#的基础生态

1.1.1 .NETFrameWork

.NETFrameWork是微软的基础生态

.NET运行框架

挑几个重要的说说:

1.1.2 COM组件与.NET组件(上图第三层)

COM组件和.NET组件都是帮助应用程序可以在windows平台上运行的底层接口。

  • 利用COM组件,可以实现非托管代码(C/C++)程序和托管代码(CLR程序)的交互
  • .NET是在基于COM组件的基础上发展而来的,是微软为了实现跨平台运行而推出的,也就是安装了. NET FrameWork的电脑都可以正常运行,而基于COM开发的程序,只能运行在Windows平台中
  • 我本人到目前为止还没有真正利用. NET框架真正开发过跨平台的应用,虽然. NET框架包含了COM组件,但是我觉得如果想要开发跨平台的应用,还是尽量避免使用COM组件,但是如果只在Windows平台上运行,则无需担心这点

1.1.3 CLR(上图第四层)

CLR(公共语言运行库 Common Language Runtime)类似于Java虚拟机,是可以让编译出来(IL中间语言)的托管代码运行在各个平台的运行时库。

它的职责是:

  • 负责资源管理和垃圾收集等等工作
  • 保证底层系统和应用之间必要的分离
说明下什么是托管代码和非托管代码
  • 托管代码:Microsoft的中间语言(IL),作用于CLR
    • 主要作用在 .NET Framework的CLR编译,只要是CLR编译器编译出来的程序集,就能够在安装有CLR程序的电脑中运行,目前应用CLR最为广泛的语言:C#、VB、F#、Iron Python、Ruby等等
    • IL通过JIT最终变为机器语言
  • 非托管代码:CLR诞生之前,那些只能在同一个系统中运行的代码,即C\C++(去想想交叉编译链多复杂吧)

注:托管、非托管代码和托管非托管资源不是一回事儿,托管、非托管资源会在GC垃圾回收机制中进行说明

什么是JIT

JIT(即时编译 Just In Time),是.NET运行可执行程序的基本方式。虽然在上图中,并没有体现出JIT,但是刚刚说了,运行的是一种中间语言即IL,而JIT编译器干的事情就是输入IL,输出的是机器语言。也就是说,只有当需要的时候,JIT即时编译器才会将之前编译好的中间语言转换为机器语言


1.1.4 框架类库FCL(上图第五层)

在C#开发过程中,可以用到大量的微软为我们提供的框架类库

基础类库BCL

Base Class Library的缩写,这里提供了一个综合性的面向对象可重用类型集合。举个例子,在开发过程中,使用的string、List容器等等等都出自BCL

WCF

Windows Communication Foundation(Windows通信开发平台),整合了原有的Windows通信的WebService、Socket、HTTP、FTP等。是.NETFramework中的SOA。通俗一点的理解,可以认为WCF是一个数据的搬运工,是一种连接各个系统的强大方式

WPF/UWP

这两项技术脱胎于经典的WinForm,但是从底层来看,两者没有任何联系,WFP的推出是微软用来取代WinForm的,有关于在Windows上开发界面的框架,发展线路可以归结为:WinForm->WPF->UWP。

  • 在WPF中,微软开发了一套类似HTML + CSS 一样的XAML语言,使得界面与后端解耦和
  • WPF是调用的图形接口是DirectX9.0,所以运行WPF程序,需要电脑显卡在硬件上至少支持DX9
  • UWP是微软针对Win10系统,基于WPF扩展而来的,不仅可以使用XAML语言,还可以使用HTML和JavaScript进行Windows界面的开发

ADO. NET\SQL

如果是一套业务系统程序,那么一定会涉及到数据库开发,这个就会用到微软为我们提供的ADO. NET程序集


1.1.5 CTS和CLS(上图第六层)

这两个不必深究,就是两个标准,使用过程中会很自然的用到。

  • CTS(Common Type System)通用类型系统,诸如类型的属性、方法、public,private访问权限,都是出自这个标准
  • CLS(Common Language Specification)公共语言规范,在编译程序的时候,编译器为我们返回的很多警告,都是来自于CLS的




2 C#核心三巨头:属性、委托、泛型

在了解了属性、委托、泛型这三种最基础的用法,那么在后续使用C#的过程中,剩下的就是去了解FCL为我们提供的各种各样的类型了,因为这些类型无外乎通过这三种方式为我们暴露接口

2.1 有关属性

推荐的C#编程原则:使用属性代替成员变量

C#通过控制成员变量的属性的访问入口,即Get、Set来控制类型的成员变量

属性带来的好处:

  • 使用属性暴露对外接口,同时可以使用属性限制访问权限
    • 设置只读、只写属性
    • 检查数据是否正确(比如年龄不能小于0)
  • 使用属性在客户端访问或设置成员的时候,能够产生一些副作用
    • 例如记录缓存、日志
    • 在属性的get\set方法中检测数据安全、线程安全等
    • 等等

2.2 有关委托

委托的实际其实就是一个函数指针,在.NET中有相当多现成的委托方法可直接使用,这些委托被应用在了很多常用类型的方法当中。灵活应用这些.NET委托可以实现回调机制、事件机制等等,这些机制都为代码设计的解耦和、减轻代码量提供了很好的解决方案。

以下是几个常用官方委托:

委托名称 说明 场景举例
Func<……> 自由功能,包含一个或多个入参,返回一个制定类型的委托 Func<string, int>:入参为string类型,返回了一个int类型结果
Predicate 是否符合条件,包含一个入参,返回一个bool类型 一般用于判断入参是否符合某个条件,例如List.FindAll(Predicate)
Action 执行动作,包含一个入参,没有返回值,即直接利用入参执行某项操作 一般用于不需要返回结果直接执行动作场景,例如List.ForEach(Action)

举个例子体会一下

在很多最为基础的BCL类型当中,比如List容器类型,都会大量的使用到委托,可以据一个例子体验一下委托的用法:

比如FindAll方法:在容器当中寻找符合特定条件的对象。而这种条件是需要程序员自定义的,而这个定义就是一个委托(即程序员“委托”List的FindAll方法,帮我找出符合我提出条件的对象)

可以使用至少三种的委托方法去实现这个委托功能:

-----实现方法一:自定义委托或框架提供的委托(例子来自MSDN)

// 声明一个Predicate委托,实现容器FindAll方法的回调机制
Predicate<Point> predicate = FindSomething;
List<Point> FindPoint = PointList.FindAll(predicate);
foreach(Point obj in FindPoint)
{
    // Do Something;
}
……

// 在其他某个地方定义的委托(函数指针)对应的函数:寻找X乘以Y大于10000的点
private static bool FindSomething(Point obj)
{
  return obj.X * obj.Y > 100000;
}


-----实现方法二:匿名函数
这种实现方法与普通委托一样,但是更加便捷,不用单独声明

Predicate<Point> predicate = delegate(Point obj) { return obj.X * obj.Y > 100000;  };
List<Point> FindPoint = PointList.FindAll(predicate);
foreach(Point obj in FindPoint)
{
    // Do Something;
}

-----实现方法三:lambda表达式

C#3.0引入了lambda表达式,Lambda表达式实际上也是一个委托

下边这个例子可以看出,lambda表达式又减少了一部分代码量,且可读性也更加清晰,但并没有体现出跨越式的优点

Predicate<Point> FindFunc2 = p => p.X * p.Y > 100000;
List<Point> FindRet2 = list4.FindAll(FindFunc2);
foreach(Point obj in FindPoint)
{
    // Do Something;
}

以下例子是直接使用lambda表达式到迭代器中,更加直接的体现出lambda表达式的优势,也减少了没有必要的代码量

foreach(Point listobj in list.Where( p => p.X * p.Y > 100000 ))
{
    // Do Something
}

2.3 有关泛型

泛型在使用上像极了C++当中的模板,在C#当中,泛型有以下几点好处

  • 避免了强制类型转换带来的装箱拆箱。在BCL中,微软也提供了大量现成的泛型类,比如使用最为广泛的List
  • 可以实现一种代码重用机制:“算法重用”,比如开发人员定义的排序算法并不需要指定某种类型,而是让调用这个算法的开发人员去指定类型

泛型的种类:

  • 泛型类型
  • 泛型接口
  • 泛型委托
  • 泛型方法

一个有关于泛型的典型例子就是有利用泛型定义一个链表,这个链表可以存储任意类型的数据:

/// <summary>
/// 抽象出链表指向Next的节点;
/// 这样就可以保证一串链表中的每一个数据都可以是不同的类型了;
/// 原因:TypeLNode<T>都被看做是LNode;
/// </summary>
public class LNode
{
    protected LNode m_Next;

    public LNode(LNode next_node)
    {
        m_Next = next_node;
    }
}

/// <summary>
/// 一个泛型类型的链表;
/// 他的好处是:不限制链表中类型的种类,可以在数据中填充任意类型;
/// </summary>
/// <typeparam name="T"></typeparam>
public class TypeLNode<T> : LNode
{
    public T m_data;
    public TypeLNode(T data) : this(data, null)
    {

    }

    /// <summary>
    /// 创建一个链表中的一个节点,需要它本身的数据和它的下一条数据;
    /// </summary>
    /// <param name="data"></param>
    /// <param name="next_node"></param>
    public TypeLNode(T data, LNode next_node) : base(next_node)
    {
        m_data = data;
    }
}
















展开阅读全文

没有更多推荐了,返回首页