接口是不能实例化的,想实例化接口,只能实例化继承了接口的类。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace InterfaceTest {
interface IMyInterface {
void AMethod();
}
class Test : IMyInterface {
public void AMethod() {
Console.WriteLine("Extends from IMyInterface");
}
}
class Program {
static void Main(string[] args) {
IMyInterface a = new Test();
a.AMethod();
Console.ReadKey();
}
}
}
如果实例化的接口引用 继承自同一个接口的不同的类,这个实例就可以调用这些类实现的该接口的所有方法。
如果你在语句中使用?yield?关键字,则意味着它在其中出现的方法、运算符或?get?访问器是迭代器。?通过使用?yield?定义迭代器,可在实现自定义集合类型的?IEnumerable?和?IEnumerator?模式时无需其他显式类(保留枚举状态的类,有关示例,请参阅?IEnumerator<T>)。
既然yield是在C#中的一个语法糖,那么就说明yield是对一种复杂行为的简化,就是将一段代码简化为一种简单的形式,方便我们程序员使用。
那么yield到底是对什么行为的简化。我们首先来看一下yield的使用场景。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
foreach (int i in Power(2, 8, ""))
{
Console.Write("{0} ", i);
}
Console.ReadKey();
}
public static IEnumerable<int> Power(int number, int exponent, string s)
{
int result = 1;
for (int i = 0; i < exponent; i++)
{
result = result * number;
yield return result;
}
yield return 3;
yield return 4;
yield return 5;
}
}
}
定义了一个返回类型是IEnumerable<int>的静态方法,方法名叫Power,如果按照平时做法,应该
return一个IEnumerablel<int>类型的参数。
public static IEnumerable<int> Power(int number, int exponent, string s)
{
int result = 1;
//接口不能实例化,我们这儿new一个实现了IEnumerable接口的List类
//接口是不能实例化的!想实例化接口,只能实例化继承了接口的类
IEnumerable<int> example = new List<int>();
for (int i = 0; i < exponent; i++)
{
result = result * number;
(example as List<int>).Add(result);
}
return example;
}
但是因为要new一个List,或者任何实现了IEnumerable接口的类型,这样也太麻烦了吧!要知道IEnumerable是一个常用的返回类型。每次使用都要new一个LIst,或者其他实现了该接口的类型。与其使用其他类型,不如我们自己定制一个实现了IEnumerable接口专门用来返回IEnumerable类型的类型。我们自己定制也很麻烦。所以微软帮我们定制好了。这个类是什么,那就是yield关键字这个语法糖。
public IEnumerable<int> Integers()
{
??? yield?return 1;
??? yield?return 2;
??? yield?return 4;
??? yield?return 8;
??? yield?return 16;
??? yield?return 16777216;
}
对Integers()的第一次调用返回1。第二次调用返回2,并且yield return 1;不再执行。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
//这儿调用了方法。
var test = Power(2, 8, "");
Console.WriteLine("Begin to iterate the collection.");
//Display powers of 2 up to the exponent of 8:
foreach (int i in Power(2, 8, ""))
{
Console.Write("{0} ", i);
}
Console.ReadKey();
}
public static IEnumerable<int> Power(int number, int exponent, string s)
{
int result = 1;
if (string.IsNullOrEmpty(s))
{
//throw new Exception("这是一个异常");
Console.WriteLine("Begin to invoke GetItems() method");
}
for (int i = 0; i < exponent; i++)
{
result = result * number;
yield return result;
}
yield return 3;
yield return 4;
yield return 5;
}
}
}
按照我们的理解当我们 var test = Power(2, 8, "");的时候确实调用了Power方法。此时应该程序打印Console.WriteLine("Begin to invoke GetItems() method");然后继续执行?Console.WriteLine("Begin to iterate the collection.");方法。所以打印顺序应该是
Begin to invoke GetItems() method
Begin to iterate the collection.
但是我们运行的时候却发现
看了反编译代码,发现我们的打印方法并没有出现在Power方法中,而是被封装进了实现枚举接口的类方法? private bool MoveNext()中。所以方法不会立即被执行,而是在我们使用数据的时候被执行。如果对此机制不了解,就容易出现另外一些意想不到的问题。例如在Power方法中添加一些验证程序,如果不符合条件就抛出一个异常。这样的异常检查不会被执行。只有我们使用数据的时候才会执行。这样就失去了检查数据的意义。
另外使用yield还有一些注意事项:
你不能在具有以下特点的方法中包含?yield return?或?yield break?语句:
异常处理
不能将?yield return?语句置于 try-catch 块中。?可将?yield return?语句置于 try-finally 语句的 try 块中。
yield break?语句可以位于 try 块或 catch 块,但不能位于 finally 块。
如果?foreach?主体(在迭代器方法之外)引发异常,则将执行迭代器方法中的?finally?块。
一个应用程序一般对应一个进程,一个进程一般有一个主线程,还有若干个辅助线程,线程之间平行运行,在线程里面可以开启协程,让程序在特定的时间内运行。
协程与协程之间是并行执行,与主线程也是并行执行,同一时间只能执行一个协程。
线程可以多线程,但是在Unity中只能在主线程中获取物体的组件、方法和游戏对象,但是协程中是可以正常获取游戏对象信息的!
在主线程中执行一个对资源消耗很大的操作时,就会影响游戏帧率,所以可以利用协程来做!
协程是通过迭代器来实现的,通过关键字IEnumerator来定义一个方法
线程拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程由操作系统调度。
协程和线程一样共享堆,不共享栈,协程由程序员在协程的代码里显示调度。
通过IEnumerator定义一个协程,通过yield return来定义返回值
IEnumerator Test(string str)
{
Debug.Log("协程被启动了!"+ str);
yield return null;
}
1.StartCoroutine(string methodName):通过协程的方法名(字符串形式)启动
2.StartCoroutine(string methodName, object values):带参数的通过方法名(字符串形式)启动
3.StartCoroutine(IEnumerator routine):通过调用方法的形式启动
void Start()
{
//通过调用方法名来启动协程
StartCoroutine("Test1");
//通过调用方法名来启动协程
StartCoroutine("Test2", "Hello World");
//直接调用方法启动协程
StartCoroutine(Test1());
StartCoroutine(Test2("Hello World"));
}
IEnumerator Test1()
{
Debug.Log("协程被启动了!");
yield return null;
}
IEnumerator Test2(string str)
{
Debug.Log("协程被启动了!" + str);
yield return null;
}
StopCoroutine和StopAllCoroutines两种方式都可以停止协程
1.StopCoroutine(string methodName):通过协程的方法名(字符串形式)关闭协程
2.StopCoroutine(IEnumerator routine):通过调用方法来关闭协程
3.StopCoroutine(Coroutine routine):通过指定的协程来关闭
4.StopAllCoroutines停止所有该脚本中启动的协程
void StopTest()
{
//第一种方式:通过调用方法的形式来关闭协程
StopCoroutine(Test1());
//第二种方式:通过方法名(字符串)来关闭协程
StopCoroutine("Test1");
//第三种方式:通过指定的协程来关闭
Coroutine a = StartCoroutine(Test1());
StopCoroutine(a);
//关闭该脚本中启动的所有协程!
StopAllCoroutines();
}
注意:使用哪一种开启协程的方法,就要用哪种方式来关闭!
void Start()
{
Debug.Log("a");
}
void Update()
{
Debug.Log("b");
StartCoroutine(Test1());
Debug.Log("d");
}
IEnumerator Test1()
{
Debug.Log("c");
yield return null;
Debug.Log("e");
}
打印:第一帧a,b,c,d,第二帧b,c,d,e
代码遇到yield return之后会暂停一帧,跳出协程,等到下一帧再根据yield的生命周期位置来进行打印。
IEnumerator Test()
{
Debug.Log("开始协程了");
yield return new WaitForSeconds(3);//等待三秒执行下方代码块
Debug.Log("三秒时间到了,执行此处代码!");
}
可以利用yield return new WairForSeconds(时间) 执行许多需要延迟的功能。
还有需要异步加载时都可以用到协程,比如AB包的异步加载、Resources资源的异步加载、场景的异步加载等。
线程和协同程序的主要不同在于:在多处理器情况下,从概念上来讲多线程程序同时运行多个线程;而协同程序是通过协作来完成,在任一指定时刻只有一个协同程序在运行,并且这个正在运行的协同程序只在必要时才会被挂起。
?