interface(接口)
接口像类一样,只不过它通过interface实现,并且它只能创建方法,不能创建字段,方法还不能实现方法体的内容。
接口用于被其它类继承,继承了接口之后,必须实现接口里的所有方法,至于方法怎么调用,方法体该写什么,由实现的类去控制和调度。
接口可以继承接口,但被类继承时,父接口的方法也需要实现。如果不实现,编译会报错。
using System;
//接口可以不继承,也可以继承接口,但是不能继承类。
//接口在被类实现的时候,需要实现接口以及接口继承的接口,所有的方法。
public interface myInterface:IInterface {
void F01() ;
void F02();
int F03();
string F04();
}
//另一个接口,用它来被上面的接口继承
public interface IInterface
{
void F00();
}
//这个类继承了接口myInterface,需要实现它的所有方法与父接口的所有方法。
public class MyClass01 : myInterface
{
public void F00()
{
throw new NotImplementedException();
}
public void F01()
{
throw new NotImplementedException();
}
public void F02()
{
throw new NotImplementedException();
}
public int F03()
{
throw new NotImplementedException();
}
public string F04()
{
throw new NotImplementedException();
}
}
abstract、virtual与override(抽象、虚方法与重写方法)
在接口中,所有方法均不可以具体实现(也就是不能实现方法体),有时候我们用起来并不方便,而抽象类就解决了这个问
题,使用关键字abstract可以声明抽象类,在抽象类中,即可定义实体数据,也可以创建实体方法。当然了,也可以通过
abstract关键字在抽象类中创建抽象方法。
值得注意的是,抽象方法只能在抽象类里实现。否则程序编译的时候会报错。
如果我们在抽象类中,定义了抽象方法,那么继承抽象类的类里,就需要用override关键字重写抽象方法
using UnityEngine;
//这个类当作组件使用,测试抽象类和继承它的类,功能是否正常。
public class ATest : MonoBehaviour
{
MyClass001 myClass = new MyClass001();
private void Start()
{
//普通类MyClass001实例的调用
myClass.Log();
myClass.aFunction();
}
}
//抽象类不能是密封或者静态的。
public abstract class C001
{
public string s = "sssss";
public void Log()
{
Debug.Log(s);
}
public abstract void aFunction();//使用abstract定义抽象方法。
}
//普通的类继承抽象类,需要实现虚方法,使用override关键字。
public class MyClass001 : C001
{
public override void aFunction()//使用override重写父类的抽象方法。
{
Debug.Log("实现了抽象类");
}
}
如果我们要使普通的类中的一些方法,也可以被继承的类重写,就可以使用virtual定义虚方法,然后继承的类使用override
关键字去实现。当然,抽象的类也可以定义虚方法。
虚方法在继承的类中,没有被重写直接调用的,还是执行父类虚方法方法体里的代码,如果被重写了,则调用重写后的代码。
//自定义的一个类C..01,包含一个虚方法。
public class Class01
{
public virtual void Test()
{
Debug.Log("哈哈");
}
}
//自定义的类C..03,包含一个虚方法。
public class Class03
{
public virtual void Test()
{
Debug.Log("哈哈");
}
}
`//创建一个类C..02,继承带有虚方法的类C..03
public class Class02: Class03
{
//可以直接调用C..01类的虚方法。
Class01 c1 = new Class01();
void C03Test()
{
c1.Test();//直接调用其它类的虚方法。
Test();//调用重写后的方法。
base.Test();//如果使用base关键字进行调用,则还是调用父类的虚方法。
}
//重写C..03类里的Test方法。
public override void Test()
{
Debug.Log("重写了Class03类里的Test方法");
}
}
数组
优点:
由于是在连续内存上存储的,所以它的索引速度非常快,访问一个元素的时间是恒定的也就
是说与数组的元素数量无关,而且赋值与修改元素也很简单。
缺点:
但是有优点,那么就一定会伴随着缺点。由于是连续存储,所以在两个元素之间插入新的元
素就变得不方便。而且就像上面的代码所显示的那样,声明一个新的数组时,必须指定其长
度,这就会存在一个潜在的问题,那就是当我们声明的长度过长时,显然会浪费内存,当我
们声明长度过短的时候,则面临这溢出的风险。这就使得写代码像是投机,针对这种缺点,
就有了后面的ArrayList。
ArrayList数组列表
为了解决数组创建时必须指定长度以及只能存放相同类型的缺点而推出的数据结构。
ArrayList是System.Collections命名空间下的一部分,所以若要使用则必须引入
System.Collections。正如上文所说,ArrayList解决了数组的一些缺点。
优点:
1.不必在声明ArrayList时指定它的长度,这是由于ArrayList对象的长度是按照其中存储的数
据来动态增长与缩减的。
2.ArrayList可以存储不同类型的元素。这是由于ArrayList会把它的元素都当做Object来处
理。因而,加入不同类型的元素是允许的。
缺点:
1.ArrayList不是类型安全的,因为把不同的类型都当做Object来做处理,很有可能会在使用
ArrayList时发生类型不匹配的情况。
2.如上文所诉,数组存储值类型时并未发生装箱,但是ArrayList由于把所有类型都当做了
Object,所以不可避免的当插入值类型时会发生装箱操作,在索引取值时会发生拆箱操作。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class P02 : MonoBehaviour {
void Start () {
//创建和实例化
ArrayList test3 = new ArrayList();
//新增数据
test3.Add("chen");
test3.Add("j");
test3.Add("d");
test3.Add("is");
test3.Add(25);
test3.Add(27);
//获取数据
string s = test3[2].ToString();
//修改数据
test3[4] = 26;
//删除数据
test3.RemoveAt(4);
//遍历
for (int i = 0; i < test3.Count; i++)
{
Debug.Log(test3[i]);
}
}
void Update () {
}
}
List集合
优点:
1.即确保了类型安全。
2.也取消了装箱和拆箱的操作。
3.它融合了Array可以快速访问的优点以及ArrayList长度可以灵活变化的优点。
List 在实际的项目开发中,会经常用到。
缺点:
没办法往List里插入其它类型的数据。实际上,我们也很少需要这样的操作。
LinkedList 链表
链表在内存存储的排序上可能是不连续的。这是由于链表是通过上一个元素指向下一个元素
来排列的,所以可能不能通过下标来访问。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class P04 : MonoBehaviour {
// Use this for initialization
void Start () {
//LinkedList对象的创建和实例化
LinkedList<int> a = new LinkedList<int>();
//AddFirst是作为容器里的第一个数据
a.AddFirst(3);
//AddLast在原有的节点末端添加数据,并作为末尾。
a.AddLast(1);
a.AddLast(4);
//通过Find查找对应数据的节点,返回类型为LinkedListNode<T>
LinkedListNode<int> cur = a.Find(3);
//AddBefore方法可以在某个节点前面插入数据,第一个参数填写对应的节点,第二个
参数填写需要插入的数据。
//而AddAfter可以在某个节点之后,添加新的节点。
a.AddBefore(cur, 2); //在3前面添加2
a.Remove(3);//移除3这个数据对应的节点
foreach (int i in a)
Debug.Log(i + " ");
//Clear可以清除所有节点
a.Clear();
//通过Count属性,获取节点个数
Debug.Log(a.Count);
}
}
优点:
1.向链表中插入或删除节点无需调整结构的容量。因为本身不是连续存储而是靠各对象的指
针所决定,所以添加元素和删除元素都要比数组要有优势。
2.链表适合在需要有序的排序的情境下增加新的元素,这里还拿数组做对比,例如要在数组
中间某个位置增加新的元素,则可能需要移动移动很多元素,而对于链表而言可能只是若干
元素的指向发生变化而已。
缺点:
1.有优点就有缺点,由于其在内存空间中不一定是连续排列,所以访问时候无法利用下标,
而是必须从头结点开始,逐次遍历下一个节点直到寻找到目标。所以当需要快速访问对象
时,数组无疑更有优势。
综上,链表适合元素数量不固定,需要经常增减节点的情况。
Queue队列
在Queue这种数据结构中,最先插入的元素将是最先被删除;反之最后插入的元素将最
后被删除,因此队列又称为“先进先出”(FIFO—first in first out)的线性表。通过使用
Enqueue和Dequeue这两个方法来实现对 Queue 的存取。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class P05 : MonoBehaviour {
// Use this for initialization
void Start () {
Queue myQ = new Queue();
myQ.Enqueue("The");//入队
myQ.Enqueue("quick");
myQ.Enqueue("brown");
myQ.Enqueue("fox");
myQ.Enqueue(null);//添加null
myQ.Enqueue("fox");//添加重复的元素
//队列的数量:Count
Debug.Log(myQ.Count);
// 遍历:打印队列中的所有值
foreach (var queue in myQ)
{
Debug.Log(queue);
}
// 打印队列中的第一个元素,并移除
Debug.Log("第一个元素为:"+myQ.Dequeue());
// 打印队列中的第一个元素
Debug.Log("第一个元素为:"+ myQ.Peek());
//移除所有的数据
myQ.Clear();
//判断队列中是否包含该数据
bool b = myQ.Contains("fox");
//将数据插入到末尾
myQ.Enqueue("最后添加的数据");
//Queue队列就是先进先出。它并没有实现 IList,ICollection。所以它不能按索引
访问元素,不能使用Add和Remove。
}
}
一些需要注意的地方:
1.先进先出的情景。
2.默认情况下,Queue的初始容量为32, 增长因子为2.0。
3.当使用Enqueue时,会判断队列的长度是否足够,若不足,则依据增长因子来增加容量,
例如当为初始的2.0时,则队列容量增长2倍。
4.在网络通信时,我们希望数据的处理是先接收先处理,则可以使用Queue进行消息的管
理。
5.队列就是先进先出。它并没有实现 IList,ICollection。所以它不能按索引访问元素,不能
使用Add和Remove(添加和删除数据)。