欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C#应该掌握的一些东西

程序员文章站 2022-04-05 08:50:09
随着培训机构的增多,越来越多的人进入IT行业。那么对于我们这些自学出来,经验不够丰富的转行者来说,我们需要掌握最起码的一些东西,这对于面试很有用,而且在工作中也很常用。本人在学习了网上各位大神关于一些知识点的总结和研究后,总结了这么一套东西。祝大家都能工作顺利,找到自己满意的工作。 重写:当一个子类 ......

随着培训机构的增多,越来越多的人进入IT行业。那么对于我们这些自学出来,经验不够丰富的转行者来说,我们需要掌握最起码的一些东西,这对于面试很有用,而且在工作中也很常用。本人在学习了网上各位大神关于一些知识点的总结和研究后,总结了这么一套东西。祝大家都能工作顺利,找到自己满意的工作。

  1. 重载和重写

重写:当一个子类继承一父类,而子类中的方法与父类中的方法的名称,参数个数、类型都完全一致时,就称子类中的这个方法重写了父类中的方法。

重载:一个类中的方法与另一个方法同名,但是参数表不同,这种方法称之为重载方法。

  1. 面向对象

封装:那么封装是什么呢? 封装就是这个人要完成一件事情,他所需要的任何工具都带在了自己的身上,所需要的技术也都装在了自己的脑子里了。 每个对象都包含它能进行操作所需要的所有信息,因此对象不必依赖其它的对象来完成自己的操作

继承:

实例化:

      (1)、先父类后子类,先静态后成员;

           实例化的时候先调用父类的静态构造快,在调用父类的构造方法,然后子类的构造块,在调用子类的构造方法;

(2) 、默认调用父类空构造;

重写  override

      (1)、重写与重载:

           重写:继承的子类中,方法签名相同( 方法名+形参个数 类型 顺序  )

           重载:同一个类 方法名相同 ,形参个数 类型 顺序 不同

      (2)、重写规则:在子类中签名与父类中相同,在合理范围内提高子类可见性;

         A、返回类型:基本类型和void必须相同;引用类型要<=父类的返回类

         B、异常:

         C、可见性:要大于或等于父类中被重写的方法

多态:

 有多态之前必须要有继承,只有多个类同时继承了同一个类,才有多态这样的说法。

同样一个方法,子类都有不同的实现方式,这个就是多态了,多态有助于程序的灵活性。

子类中如果重写了父类的方法(多态),那么父类中的这个方法将不会再调用。

编译时多态:其实就是重载,是对非虚成员来说的,系统在编译时,根据不同签名来决定实现何种操作。

运行时多态:也就是重写,是通过虚成员实现的,指直到系统运行时,才根据实际情况决定实现何种操作。

第二种情况是在继承的基础上实现的,子类继承基类时,通过对虚成员的重写,然后利用基类引用子类对象,那么不同的子类对象实现相应的不同操作。

这样的好处是显而易见的,利用基类类型定义一次,然后给它传入不同的子类对象,然后实现不同的操作,提高了效率。

3.值类型和引用类型

值类型:

byte,short,int,long,float,double,decimal,char,bool 和 struct 统称为值类型。

引用类型:

string 和 class统称为引用类型。

1.值类型存储在内存栈中,引用类型数据存储在内存堆中,而内存单元中存放的

是堆中存放的地址。

2.值类型存取快,引用类型存取慢。

3.值类型表示实际数据,引用类型表示指向存储在内存堆中的数据的指针和引用。

4.栈的内存是自动释放的,堆内存是.NET 中会由 GC 来自动释放。

5.值类型继承自 System.ValueType,引用类型继承自 System.Object

4.数据结构

数组Array:连续存储  
数组是最简单的数据结构。其具有如下特点:
数组存储在连续的内存上。
数组的内容都是相同类型。
数组可以直接通过下标访问。

int size = 5;
int[] test = new int[size];

//赋值
test2[0] = "chen";

//修改
test2[0] = "chenjd";

声明一个新的数组时,必须指定其长度

ArrayList:  
为了解决数组创建时必须指定长度以及只能存放相同类型的缺点而推出的数据结构。可以存储不同类型的元素。这是由于ArrayList会把它的元素都当做Object来处理。因而,加入不同类型的元素是允许的。

ArrayList test3 = new ArrayList();

//新增数据
test3.Add("chen");
test3.Add("j");
test3.Add("d");
test3.Add("is");
test3.Add(25);
//修改数据
test3[4] = 26;
//删除数据
test3.RemoveAt(4);

所谓装箱 (boxing):就是值类型实例到对象的转换那么拆箱:就是将引用类型转换为值类型

List<T>泛型List  
为了解决ArrayList不安全类型与装箱拆箱的缺点,所以出现了泛型的概念,作为一种新的数组类型引入。ArrayList很相似,长度都可以灵活的改变,最大的不同在于在声明List集合时,我们同时需要为其声明List集合内数据的对象类型

List<string> test4 = new List<string>(); 

Dictionary<K,T> 

字典的实现方式就是哈希表的实现方式当创建字典时,必须声明key和item的类型我们新建了一个空的字典,那么伴随而来的是2个长度为3的数组

  1. 接口和抽象类

C#基础之接口

接口的定义是指定一组函数成员而不实现成员的引用类型,其它类型和接口可以继承接口。

(1) 通过接口可以实现多重继承,C#接口的成员不能有public、protected、internal、private等修饰符。原因很简单,接口里面的方法都需要由外面接口实现去实现方法体,那么其修饰符必然是public。C#接口中的成员默认是public的,java中是可以加public的。

(2) 接口成员不能有new、static、abstract、override、virtual修饰符。有一点要注意,当一个接口实现一个接口,这2个接口中有相同的方法时,可用new关键字隐藏父接口中的方法。

(3) 接口中只包含成员的签名,接口没有构造函数,所有不能直接使用new对接口进行实例化。接口中只能包含方法、属性、事件和索引的组合。接口一旦被实现,实现类必须实现接口中的所有成员,除非实现类本身是抽象类。

(4) C#是单继承,接口是解决C#里面类可以同时继承多个基类的问题。

class Program

    {

        static void Main(string[] args)

        {

            IWorker james1 = new James1();

            IWorker james2 = new James2();

            james1.work("设计");

            james2.work("编程");

            //从这个例子我体会到了有接口的好处,可以想象如果又来了新的员工。

            //如果不采用接口,而是每个员工都有一个单独的类,这样就会容易出错。

            //如果有接口这种协议约束的话,那么只要实现了接口就肯定有接口里声明的方法,我们只需拿来调用。        }

    }

    public interface IWorker{ void work(string s); }

    class James1 : IWorker

    {

        public void work(string s)

        {

            Console.WriteLine("我的名字是James1,我的工作是" +s);

        }

    }

    class James2 : IWorker

    {

        public void work(string s)

        {

            Console.WriteLine("我的名字是James2,我的工作是"+s);

        }

}

修饰符 interface 接口名
{
  //接口主体
}
1、一个接口就相当于一个抽象类,但是它不能半喊任何实现方法。
2、接口的每种方法都必须在派生类中实现。
3、接口有时候可以看成是类的模具,它指明一个类该提供哪些内容。
4、接口主体只限于方法、索引器、属性的声明。
5、接口中不能包含字段、构造函数和常量等。
6、接口成员是隐式公开的,如果对其显式指定访问级别,就会出现编译器错误。
7、在接口中不能实现任何方法,属性或者索引器。
8、在指定方法时,只需给出返回类型、名称和参数列表,然后以分号结束。
9、实现接口的语法与实现继承一样,都用冒号“:”
示例
interface Icustomer
{
………………
}
public class MyClass: Icustomer
{
………………
}
10、接口中的方法不能重写,只能实现。
11
编码标准:
接口名称需始终冠以大写字母I

什么是抽象类:不能被实例化的类称为抽象类,抽象类是派生类的基类。

abstract class 类名
{
…………
}
1、一个抽象类可以同时包含抽象方法和非抽象方法。
2、抽象方法只在派生类中真正实现,这表明抽象方法只存放函数原型,不涉及主体代码,
3、派生自抽象类的类需要实现其基类的抽象方法,才能实例化对象。
4、使用override关键子可在派生类中实现抽象方法,经override声明重写的方法称为重写基类方法,其签名必须与override方法的签名相同。

示例:
using System;
namespace Example_5
{
//抽象类
abstract class ABC
{
  //抽象方法
  public abstract void Afunc();
}
//派生类
class Derv:ABC
{
  //实现抽象类中的抽象方法
  public override void Afunc()
{
   Console.WriteLine(“实现抽象方法”);
  }
}
public class Test
{
  static void Main(string[] args)
  {
   Derv obj=new Derv();
   obj.Afunc();
  }
}
}
5、基类实现抽象类,则派生类不需要重新实现该抽象类。
6、抽象类并不仅仅只是一种实现技巧,它更代表一种抽象的概念,从而为所有的派生类确立一种约定。

接口和抽象类的区别

接口用于规范,抽象类用于共性。抽象类是类,所以只能被单继承,但是接口却可以一次实现多个。
接口中只能声明方法,属性,事件,索引器。而抽象类中可以有方法的实现,也可以定义非静态的类变量。
抽象类可以提供某些方法的部分实现,接口不可以。抽象类的实例是它的子类给出的。接口的实例是实现接口的类给出的。
在抽象类中加入一个方法,那么它的子类就同时有了这个方法。而在接口中加入新的方法,那么实现它的类就要重新编写(这就是为什么说接口是一个类的规范了)。
接口成员被定义为公共的,但抽象类的成员也可以是私有的、受保护的、内部的或受保护的内部成员(其中受保护的内部成员只能在应用程序的代码或派生类中访问)。此外接口不能包含字段、构造函数、析构函数、静态成员或常量。

  1. 委托和事件

委托:可以理解为将一个方法变成了参数类型,属性。下面,我们直接上代码,在代码中理解委托的运用和定义。

namespace Delegate {

//定义委托,它定义了可以代表的方法的类型

 public delegate void GreetingDelegate(string name);

class Program {

Private static void EnglishGreeting(string name) {

Console.WriteLine("Morning, " + name);

}

private static void ChineseGreeting(string name)

{

Console.WriteLine("早上好, " + name);

 }

//注意此方法,它接受一个GreetingDelegate类型的方法作为参数 

private static void GreetPeople(string name, GreetingDelegate MakeGreeting)

{

MakeGreeting(name); }

static void Main(string[] args)

{

GreetPeople("Jimmy Zhang", EnglishGreeting); GreetPeople("张子阳", ChineseGreeting); Console.ReadKey(); }

委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。

事件它封装了委托类型的变量,使得:在类的内部,不管你声明它是public还是protected,它总是private的。在类的外部,注册“+=”和注销“-=”的访问限定符与你在声明事件时使用的访问符相同。

声明一个事件不过类似于声明一个进行了封装的委托类型的变量而已。

7.设计模式

单例模式

确保一个类只有一个实例,并提供一个全局访问点。

  1. 通过定义一个静态私有变量来记录单例类的唯一实例。
  2. 私有方法来防止外界使用new关键字来创建该类实例。
  3. 公有方法来提供该类实例的唯一全局访问点
  4. 假如实例不存在则new一个新的实例,否则返回已有实例。

对于多线程来说则定义一个线程标识保证线程同步,接着加一个线程锁,多一句判断就行了。

观察者模式

Observer设计模式中主要包括如下两类对象:

  1. Subject:监视对象,它往往包含着其他对象所感兴趣的内容。
  2. Observer:监视者,它监视Subject,当Subject中的某件事发生的时候,会告知Observer,而Observer则会采取相应的行动。

Observer设计模式是为了定义对象间的一种一对多的依赖关系,以便于当一个对象的状态改变时,其他依赖于它的对象会被自动告知并更新。Observer模式是一种松耦合的设计模式。

下面我们开始上代码:

 C#应该掌握的一些东西

C#应该掌握的一些东西

C#应该掌握的一些东西

 

8.排序算法

冒泡排序:

 C#应该掌握的一些东西

C#应该掌握的一些东西

 

快速排序:

一趟快速排序的算法是:
1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;
2)以第一个数组元素作为关键数据,赋值给key,即key=A[0];
3)从j开始向前搜索,即由后开始向前搜索(j–),找到第一个小于key的值A[j],将A[j]和A[i]互换;
4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]互换;
5)重复第3、4步,直到i=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)。

 C#应该掌握的一些东西

 

 C#应该掌握的一些东西

 

插入排序:

从第一个元素开始,该元素可以认为已经被排序

取出下一个元素,在已经排序的元素序列中从后向前扫描

如果该元素(已排序)大于新元素,将该元素移到下一位置

重复步骤3,直到找到已排序的元素小于或者等于新元素的位置

将新元素插入到下一位置中

重复步骤2~5

 C#应该掌握的一些东西

 

选择排序:

首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

选择排序的主要优点与数据移动有关。如果某个元素位于正确的最终位置上,则它不会被移动。选择排序每次交换一对元素,它们当中至少有一个将被移到其最终位置上,因此对n个元素的表进行排序总共进行至多n-1次交换。在所有的完全依靠交换去移动元素的排序方法中,选择排序属于非常好的一种。

 

 C#应该掌握的一些东西

 

 

 

9.线程

1、基本概念

进程(Process)是Windows系统中的一个基本概念,它包含着一个运行程序所需要的资源。一个正在运行的应用程序在操作系统中被视为一个进程,进程可以包括一个或多个线程。线程是操作系统分配处理器时间的基本单元,在进程中可以有多个线程同时执行代码。进程之间是相对独立的,一个进程无法访问另一个进程的数据(除非利用分布式计算方式),一个进程运行的失败也不会影响其他进程的运行,Windows系统就是利用进程把工作划分为多个独立的区域的。进程可以理解为一个程序的基本边界。是应用程序的一个运行例程,是应用程序的一次动态执行过程。

线程(Thread)是进程中的基本执行单元,是操作系统分配CPU时间的基本单位,一个进程可以包含若干个线程,在进程入口执行的第一个线程被视为这个进程的主线程。在.NET应用程序中,都是以Main()方法作为入口的,当调用此方法时系统就会自动创建一个主线程。线程主要是由CPU寄存器、调用栈和线程本地存储器(Thread Local StorageTLS)组成的。CPU寄存器主要记录当前所执行线程的状态,调用栈主要用于维护线程所调用到的内存与数据,TLS主要用于存放线程的状态信息。

创建多线程的步骤:
1、编写线程所要执行的方法
2、实例化Thread类,并传入一个指向线程所要执行方法的委托。(这时线程已经产生,但还没有运行)
3、调用Thread实例的Start方法,标记该线程可以被CPU执行了,但具体执行时间由CPU决定

 

 

10.协同程序

一。什么是协同程序

       协同程序,即在主程序运行时同时开启另一段逻辑处理,来协同当前程序的执行。换句话说,开启协同程序就是开启一个线程

二。协同程序的开启与终止

       Unity3D中,使用MonoBehaviour.StartCoroutine方法即可开启一个协同程序,也就是说该方法必须在MonoBehaviour或继承于MonoBehaviour的类中调用。

       Unity3D中,使用StartCoroutine(string methodName)StartCoroutine(IEnumerator routine)都可以开启一个线程。区别在于使用字符串作为参数可以开启线程并在线程结束前终止线程,相反使用IEnumerator 作为参数只能等待线程的结束而不能随时终止(除非使用StopAllCoroutines()方法);另外使用字符串作为参数时,开启线程时最多只能传递 一个参数,并且性能消耗会更大一点,而使用IEnumerator 作为参数则没有这个限制。

        Unity3D中,使用StopCoroutine(string methodName)来终止一个协同程序,使用StopAllCoroutines()来终止所有可以终止的协同程序,但这两个方法都只能终止该 MonoBehaviour中的协同程序。

        还有一种方法可以终止协同程序,即将协同程序所在gameobjectactive属性设置为false,当再次设置activeture时,协同程 序并不会再开启;如是将协同程序所在脚本的enabled设置为false则不会生效。这是因为协同程序被开启后作为一个线程在运行,而 MonoBehaviour也是一个线程,他们成为互不干扰的模块,除非代码中用调用,他们共同作用于同一个对象,只有当对象不可见才能同时终止这两个线 程。

粗浅小结:

1.Coroutines顾名思议是用来协助主要进程的,在Unity中感觉就是一个可动态添加和移除的Update()函数。它的调用在所有Update函数之后。

2.yield就像是一个红绿灯,在满足紧跟在它后面的条件之前,这个协程会挂起,把执行权交给调用它的父函数,满足条件时就可以执行yield下面的代码。

协同的用法

Yield中断:(有中断就代表程序停在该处,等待yield后的时间结束再继续执行后面的语句。)

unity C#yield(中断)语句必须要在IEnumerator类型里

C#方法,方法的返回类型为IEnumerator返回值如(eg: yield return new WaitForSeconds(2); 或者 yield return null;)

yields不可以在Update或者FixedUpdate里使用。

Coroutine协同:(为什么要有Coroutine的概念? 因为有中断的存在,试想一个程序都靠Yield来暂停的话,如何做到一个事件暂停的过程中,暂停过程中继续执行后面的程序? 那么就要靠Coroutine来实现。)

  • 可以在UPdate或者FixedUpdate里使用coroutine
  • 这里有两种:StartCoroutine(协同工作) 和 yield return StartCoroutine(中断式的协同工作)
  • yield的代表先执行完本语句(不管多长时间),或者执行完本yield方法调用,才执行后续语句。例如StartCoroutine(WaitAndPrint(2.0F)),继续执行StartCoroutine后面的语句,
  • 没有yield的代表继续顺序执行。例如:yield return StartCoroutine(WaitAndPrint(2.0F)),代表StartCoroutine(WaitAndPrint(2.0F))函数里等待全部执行完,再执行StartCoroutine后面的语句

using UnityEngine;using System.Collections;

public class example : MonoBehaviour {

    IEnumerator Start() {

        print("Starting " + Time.time);

        yield return StartCoroutine(WaitAndPrint(2.0F));

        print("Done " + Time.time);

    }

    IEnumerator WaitAndPrint(float waitTime) {

        yield return new WaitForSeconds(waitTime);

        print("WaitAndPrint " + Time.time);

    }

}

using UnityEngine;using System.Collections;

public class example : MonoBehaviour {

    void Start() {

    print("Starting " + Time.time);

        StartCoroutine(WaitAndPrint(2.0F));

        print("Before WaitAndPrint Finishes " + Time.time);

    }

    IEnumerator WaitAndPrint(float waitTime) {

        yield return new WaitForSeconds(waitTime);

        print("WaitAndPrint " + Time.time);

    }

}

using UnityEngine;using System.Collections;

public class example : MonoBehaviour {

    IEnumerator Do() {

        print("Do now");

        yield return new WaitForSeconds(2);

        print("Do 2 seconds later");

    }

    IEnumerator Awake() {

        yield return StartCoroutine("Do");    //Yield StartCoroutine就代表中断式的协同工作

        print("Also after 2 seconds");

        print("This is after the Do coroutine has finished execution");

    }

}

using UnityEngine;using System.Collections;

public class example : MonoBehaviour {public static IEnumerator Do() {

  print("Do now");

  yield return new WaitForSeconds(2);

  print("Do 2 seconds later");

}void Awake() {

  Do();    //执行DO,但是do后的语句继续执行

  print("This is printed immediately");

}