==相同点==
1. 都不能直接的被实例化。
2. 都可以被继承。
3. 都可以用继承的方式在子类里面实现对应的抽象方法。
4. 都可以遵循里氏替换原则
==不同点==
1.接口完全不能被实例化,但是抽象类可以通过子类实现间接的实例化。
2.接口是完全抽象,抽象类可以是部分抽象,类里面可以写方法的具体实现。
3.抽象类只能被单继承,接口可以被多继承。
4.抽象类里面可以有成员变量,接口里面不能有成员变量。
5.抽象类有构造函数,接口没有。
class Father
{
public int fs;
public Father()
{
Console.WriteLine("执行父亲的无参构造函数");
}
}
class Son : Father
{
public int cs;
//下面这两个是等效的
public Son(int b)
{
Console.WriteLine("执行儿子的有参构造函数");
cs = b + 10;
}
public Son(int b):base()
{
Console.WriteLine("执行儿子的有参构造函数");
cs = b + 10;
}
}
private struct Entry
{
public int hashCode; // 除符号位以外的31位hashCode值, 如果该Entry没有被使用,那么为-1
public int next; // 下一个元素的下标索引,如果没有下一个就为-1
public TKey key; // 存放元素的键
public TValue value; // 存放元素的值
}
bucketIndex = HashFunc(key1) % 8
private int[] buckets; // Hash桶
private Entry[] entries; // Entry数组,存放元素
private int count; // 当前entries的index
private int version; // 当前版本,防止迭代过程中集合被更改
private int freeList; // 被删除Entry在entries中的下标index,这个位置是空闲的
private int freeCount; // 有多少个被删除的Entry,有多少个空闲的位置
private IEqualityComparer<TKey> comparer; // 比较器
private KeyCollection keys; // 存放Key的集合
private ValueCollection values; // 存放Value的集合
// Avoid awfully small sizes
int hashsize = (rawsize > InitialSize) ? HashHelpers.GetPrime((int)rawsize) : InitialSize;
buckets = new bucket[hashsize];
-----
private const Int32 InitialSize = 3;
public const int HashCollisionThreshold = 100;
private void Resize()
{
Resize(HashHelpers.ExpandPrime(count), false);
}
private void Resize(int newSize, bool forceNewHashCodes)
{
Contract.Assert(newSize >= entries.Length);
int[] newBuckets = new int[newSize];
for (int i = 0; i < newBuckets.Length; i++) newBuckets[i] = -1;
Entry[] newEntries = new Entry[newSize];
-------
此处省略,详细代码见https://referencesource.microsoft.com/#mscorlib/system/collections/generic/dictionary.cs,1dca11c8648f5d65
-------
}
我们主要关注其中的newSize这个变量,就是这个变量决定了扩容之后两个关键数组的大小。我们发现是先将当前的容量大小传入ExpandPrime这个函数。
public static int ExpandPrime(int oldSize)
{
int newSize = 2 * oldSize;
// Allow the hashtables to grow to maximum possible size (~2G elements) before encoutering capacity overflow.
// Note that this check works even when _items.Length overflowed thanks to the (uint) cast
if ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize)
{
Contract.Assert( MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength");
return MaxPrimeArrayLength;
}
return GetPrime(newSize);
}
}
而这个函数显示将老的容量翻倍赋给新的容量,那么Dictionary的扩容是不是就在原先的基础上翻倍的呢,其实不然,我们继续看可以发现返回值是GetPrime(newSize),继续进这个函数去看。
public static int GetPrime(int min)
{
if (min < 0)
throw new ArgumentException(Environment.GetResourceString("Arg_HTCapacityOverflow"));
Contract.EndContractBlock();
for (int i = 0; i < primes.Length; i++)
{
int prime = primes[i];
if (prime >= min) return prime;
}
//outside of our predefined table.
//compute the hard way.
for (int i = (min | 1); i < Int32.MaxValue;i+=2)
{
if (IsPrime(i) && ((i - 1) % Hashtable.HashPrime != 0))
return i;
}
return min;
}
我们发现这是一个取素数的函数,首先会从给定的Primes这个数组中取离原先容量翻倍后数值最接近的素数,如果超过了Primes数组给定的最大值,那么就会直接去寻找离翻倍数值最近的素数,并且改素数减一不是HashPrime(101)的倍数。
public static readonly int[] primes =
{
3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919,
1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591,
17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437,
187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263,
1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369};
总结:Dictionary的本质为两个数组,解决冲突的方式是拉链法,初始化的容量是3,每次扩容先从Primes数组中获取,超过7199369后,扩容为原容量的两倍大相近的素数。
public IEnumerator CS
{
Console.WriteLine("协程启动1");
yield return null;
Console.WriteLine("协程启动2");
yield return break;
}
void Start()
{
StartCroutine(CS());
}
public class Szcs : MonoBehaviour
{
myClass myclass = new myClass();
private void Start()
{
StartCoroutine(myclass.GetEnumerator());
foreach (var item in myclass)
{
Debug.Log(item);
}
}
}
class myClass
{
public IEnumerator GetEnumerator()
{
Debug.Log("cs");
yield return new WaitForSeconds(1.0f);
Debug.Log("One Seconds later");
yield return 12;
}
}
class CS
{
//一个返回值是IEnumerator的方法
//yield语法糖会自动构建IEnumerator
//协程1
public IEnumerator enumerableFuc1()
{
int i = 0;
Console.WriteLine("Enumerator1:" + i);
yield return i;
i++;
Console.WriteLine("Enumerator1:" + i);
yield return i;
i++;
Console.WriteLine("Enumerator1:" + i);
yield break;
}
//协程2
public IEnumerator enumerableFuc2()
{
int i = 100;
Console.WriteLine("Enumerator2:" + i);
yield return i;
i++;
Console.WriteLine("Enumerator2:" + i);
yield return i;
i++;
Console.WriteLine("Enumerator2:" + i);
yield return i;
Console.WriteLine("继续执行");
yield break;
}
}
class CoroutinesManager
{
public List<IEnumerator> coroutines = new List<IEnumerator>();
public void StartCoroutine(IEnumerator coroutine)
{
coroutines.Add(coroutine);
}
}
class Program
{
static void Main(string[] args)
{
int i = 0;
CoroutinesManager coroutinesManager = new CoroutinesManager();
CS cs = new CS();
//注册协程
//在开启协程的时候把协程注册到维护的表里面去
coroutinesManager.StartCoroutine(cs.enumerableFuc1());
coroutinesManager.StartCoroutine(cs.enumerableFuc2());
//模拟Unity生命周期循环
while (true)
{
Console.WriteLine("frame:" + i++);
//判断协程容器中是否还存在协程
if (coroutinesManager.coroutines.Count <= 0)
{
return;
}
List<IEnumerator> delete = new List<IEnumerator>();
foreach (var item in coroutinesManager.coroutines)
{
//如果item的MoveNext不为空
if (!item.MoveNext())
{
delete.Add(item);
}
}
foreach (var item in delete)
{
coroutinesManager.coroutines.Remove(item);
}
}
}
}
--基础父亲表
Father = {}
Father.state = "元表"
--实现父亲表的new方法
function Father:new()
--创建实体父亲表
local father = {}
--将父亲表设置为实体父亲表的元表
setmetatable(father,self)
--设置父亲表的__index保证父亲表中的变量能被实体父亲访问到
self.__index = self
--将设置好的实体父亲表返回
return father
end
--实现父亲表的说话方法
function Father:Speak()
print(self.state.."说话了")
end
--实现父亲表的继承方法
function Father:Inherit(sonName)
--通过大G表创造出子表实例
_G[sonName] = {}
local son = _G[sonName]
--将父亲表设置为子表的元表
setmetatable(son, self)
--实现base来复用父类方法
son.base = self
end
--实例化一个父亲表
father = Father:new()
father.state = "父亲"
father:Speak()
--设置Boy表继承于father表
Father:Inherit("Boy")
--设置Boy表的信息
Boy.state = "儿子"
--重写Boy表里面的Speak方法
function Boy:Speak()
--这种调用父类方法时会使用子类字段
self.base.Speak(self)
--这种调用父类方法时会使用父类字段(慎用)
self.base:Speak(self)
--子类自己的方法
print("我是儿子"..self.state.."说话了")
end
--实例化一个儿子表
boy = Boy:new()
boy:Speak()
father:Speak()
__index 索引查询
如果有表中不存在的字段内容,就会去表的元表的__index中去寻找
__newindex 索引更新
如果有表中不存在的字段内容设置,就会去表的原表中的__newindex中去新建并设置
rawset() 更新
这个方法会不触发任何元方法进行字段跟新
rawget() 查询
这个方法不触发任何元方法进行字段查询
__call
如果元表中定义了__call,那么就可以通过变量名来当做函数来调用
class = setmetatable({},{__call = function (self,index)
print(index)
end})
class(1) --打印1
__tostring
可以更改表的输出行为
__add = B, --加法
__sub = B, --减
__mul = B, --乘
__div = B, --除
__mod = B, --取模
__pow = B, --乘幂
__unm = B, --取反
__concat = B, --连接
__len = B, --长度
__eq = B, --相等
__lt = B, --小于
__le = B, --小于等于
白色(新)
是当GC的标记阶段结束但是清扫阶段没开始的时候给新被建立的对象标记的状态。此时应为并没有发现此对象的引用关系,所以会被标记成白色,理应被清除掉,但很明显这是不合理的,所以会白色(新)状态,GC在Sweep阶段只会删除白色(旧)状态的对象,而在Sweep阶段结束后白色(新)就会转变成白色(旧)状态。
白色(旧的)
可回收状态
灰色
中间状态——当前对象在Mark阶段已被访问,但是该对象引用的其他对象还没有访问完。
黑色
不可回收状态——当前对象和该对象引用的所有对象都已经被标记。
3.GC流程
首先在lua的全局状态机(global_State)中存在着几个与GC流程息息相关的链表。这里只简单说明一下接下来要说到的链表。
分别是
(简要说明–实际内容更复杂
可以看这篇文章本节内容也参考此文: lua垃圾回收机制)
unity面试——Lua 闭包 这个大佬已经说的很详细了。