Action和Func参数
Action和Func是隐式定义的委托,一般情况下两者都会做为方法参数实例,参与到方法方法定义与执行中;Action作为参数实例时,是执行方法时必须执行Action参数所委托的方法;Func作为参数实例时,是获取Func参数所委托方法的执行结果,该执行结果将参与到方法方法定义与执行中。
Action和Func作为参数的典型应用是在Task.Run方法的定义与执行中。
Action和Func参数一般情况下委托的是异步方法,同步方法如非必要不会作为Action和Func参数进行使用,因为这会使方法的可理解性变差,所以Task.Run方法一般情况下的中的Action和Func参数所委托的方法是异步方法,对于“Task.Run”理解见:Task.Run()方法总结。
Task.Run是理解Action和Func参数的一个方面但是不够0基础,下面本人将以从0基础上来构建并理解Action和Func参数:
public class ActionParam
{
/// <typeparam name="T">泛型类型实例(1个指定类的类型实例)。</typeparam>
/// <param name="action">带1个参数的Action委托方法实例参数。</param>
/// <param name="p">1个指定类的1个实例。</param>
/// <summary>
/// 【测试】
/// <remarks>
/// 摘要:
/// 自定义测试带1个参数的Action委托方法实例参数。
/// </remarks>
/// </summary>
public static void Test<T>(Action<T> action, T p)
{
action(p);
}
/// <param name="s">1个整型值。</param>
/// <summary>
/// 【行为】
/// <remarks>
/// 摘要:
/// 带有1个整型参数值的无返回方法。
/// </remarks>
/// </summary>
public static void Action(int s)
{
WriteLine(s);
}
/// <param name="s">1个字符串类型值。</param>
/// <summary>
/// 【行为】
/// <remarks>
/// 摘要:
/// 带有1个字符串类型参数值的无返回方法。
/// </remarks>
/// </summary>
public static void Action(string s)
{
WriteLine(s);
}
/// <summary>
/// 【行为无参数】
/// <remarks>
/// 摘要:
/// 无参数值的无返回方法。
/// </remarks>
/// </summary>
public static void ActionNoParam()
{
WriteLine("This is ActionNoParam");
}
/// <summary>
/// 【异步行为无参数】
/// <remarks>
/// 摘要:
/// 无参数值的无返回方法。
/// </remarks>
/// </summary>
public static async void ActionNoParamAsync()
{
await Task.Delay(1000);
WriteLine("This is ActionNoParamAsync.");
}
}
public class FuncParam
{
/// <typeparam name="T1">泛型类型实例(1个指定类的类型实例)。</typeparam>
/// <typeparam name="T1">泛型类型实例(另1个指定类的类型实例)。</typeparam>
/// <typeparam name="TResult">泛型返回值实例。</typeparam>
/// <param name="func">带2个参数的Func委托方法实例参数。</param>
/// <param name="a">1个指定类的1个实例。</param>
/// <param name="b">另1个指定类的1个实例。</param>
/// <summary>
/// 【测试】
/// <remarks>
/// 摘要:
/// 自定义测试带2个参数的Func委托方法实例参数。自定义测试带2个参数的Func委托方法实例参数。
/// </remarks>
/// <returns>
/// 返回:
/// 通过Func委托方法实例参数实例,获取的泛型返回值实例。
/// </returns>
/// </summary>
public static TResult Test<T1, T2, TResult>(Func<T1, T2, TResult> func, T1 a, T2 b)
{
return func(a, b);
}
/// <param name="a">1个指定的整型数据值。</param>
/// <param name="b">另1个指定的整型数据值。</param>
/// <summary>
/// 【方法】
/// <remarks>
/// 摘要:
/// 通过相应的参数实例,获取1个指定的整型数据值。
/// </remarks>
/// <returns>
/// 返回:
/// 1个整型数据值。
/// </returns>
/// </summary>
public static int Fun(int a, int b)
{
return a + b;
}
/// <param name="a">1个指定的字符串。</param>
/// <param name="b">另1个指定的字符串。</param>
/// <summary>
/// 【方法】
/// <remarks>
/// 摘要:
/// 通过相应的参数实例,拼接1个指定的字符串。
/// </remarks>
/// <returns>
/// 返回:
/// 拼接后的1个指定的字符串。
/// </returns>
/// </summary>
public static string Fun(string a, string b)
{
return a = a + " " + b;
}
/// <summary>
/// 【异步方法无参数】
/// <remarks>
/// 摘要:
/// 异步获取1个指定的无参数的异步方法实例的执行结果。
/// </remarks>
/// <returns>
/// 返回:
/// 1个指定的无参数的异步方法实例的执行结果。
/// </returns>
/// </summary>
public static async Task FuncNoParamAsync()
{
await Task.Delay(1000);
WriteLine("This is FuncNoParamAsync.");
}
/// <param name="func">无参数的Func委托异步方法实例参数。</param>
/// <summary>
/// 【异步方法无参数】
/// <remarks>
/// 摘要:
/// 获取1个指定的无参数的Func委托方法实例的执行结果。
/// </remarks>
/// <returns>
/// 返回:
/// 1个指定的无参数的Func委托异步方法实例的执行结果。
/// </returns>
/// </summary>
public static Task FuncNoParamTask(Func<Task> func)
{
func();
return Task.CompletedTask;
}
}
using static System.Console;
using ActionAndFunc;
//===========================================Action作为参数>>>>>Task.Run=====================================================
await Task.Run(() => ActionParam.ActionNoParam());
await Task.Run(() => ActionParam.ActionNoParamAsync());
await Task.Run(async () => /*await */ ActionParam.ActionNoParamAsync());//不能定义“await”关键字,否则会出现语法异常。
WriteLine("ActionNoParamAsync_IsCompleted:{0}", Task.Run(() => ActionParam.ActionNoParamAsync()).IsCompleted);
WriteLine("ActionNoParamAsync_IsCompleted:{0}", Task.Run(async () => /*await */ ActionParam.ActionNoParamAsync()).IsCompleted);
//===========================================Action作为参数>>>>>自定义=====================================================
ActionParam.Test<string>(ActionParam.Action, " ActionParam");
ActionParam.Test<int>(ActionParam.Action, 1000);
ActionParam.Test<string>(p => { WriteLine("{0}", p); }, " ActionParam_Lambda");//使用Lambda表达式定义委托。
//==========================================Func作为参数>>>>>Task.Run=====================================================
await Task.Run(() => FuncParam.FuncNoParamAsync());
await Task.Run(async () => await FuncParam.FuncNoParamAsync());
WriteLine("Task.Run_IsCompleted(FuncNoParamAsync):{0}", Task.Run(() => FuncParam.FuncNoParamAsync()).IsCompleted);
WriteLine("Task.Run_IsCompleted(FuncNoParamAsync):{0}", Task.Run(async () => await FuncParam.FuncNoParamAsync()).IsCompleted);
Task _taskRunFuncNoParamAsync = Task.Run(async () => await FuncParam.FuncNoParamAsync());
await _taskRunFuncNoParamAsync;
WriteLine("_taskRunFuncNoParamAsync_IsCompleted:{0}", _taskRunFuncNoParamAsync.IsCompleted);
//===========================================Func作为参数>>>>>自定义=====================================================
WriteLine("FuncNoParamAsync_IsCompleted:{0}", FuncParam.FuncNoParamTask(()=>FuncParam.FuncNoParamAsync()).IsCompleted);//等于_taskRunFuncNoParamAsync。
WriteLine(FuncParam.Test<int, int, int>(FuncParam.Fun, 100, 200));
WriteLine(FuncParam.Test<string, string, string>(FuncParam.Fun, "This", "is"));
WriteLine(FuncParam.Test<int, int, int>((int a, int b) => FuncParam.Fun(a, b), 1000, 2000));//使用Lambda表达式定义委托。
ReadKey();
ValueTask
ValueTask取代Task<Tresult>应用的N种场景
1、Func参数委托异步方法,获取相应的果时,如果使用Task<Tresult>则会造成阻塞,而使用ValueTask则不会。
2、Task<TResult>返回的是1个带有返回值的Task实例,该Task实例是以内存“堆”的形式进行存储(以Class(类)进行存储),同时由于Task继承于Idisposable接口,所以所有的Task实例以需要通过额外的与之相对的GC(Garbage Collection)来负责内存的释放。ValueTask是用来直接获取Task<TResult>中的TResult,即ValueTask实例只获取异步方法的返回值,ValueTask实例(Task<TResult>的返回)是以内存“栈”的形式进行存储(以Struct(结构体)进行存储),ValueTask继承于IEquatable<ValueTask<TResult>>,可见ValueTask是实例值,而非对象值。
栈:或多或少负责跟踪正在程序中运行的代码。栈空间比较小,但是读取速度快。
堆:或多或少负责跟踪程序对象或数据。堆空间比较大,但是读取速度慢。
数据存储方式的根本差异,决定了Task<TResult>实例与ValueTask实例读取速度的根本差异,如果通过异步方法,对1个指定实体的多个实例进行数据筛选时,最好使用ValueTask,例如:System.Linq中的大部分异步方法的返回实例,都由Task<TResult>被替换为了ValueTask,因为1个指定实体的多个实例已经被存储到了内存中或已经被缓存到了分布式缓存数据库中,所以最好使用ValueTask。
/// <summary>
- 异步方式只获取返回的结果(关键的重点是返回结果)则用ValueTask,如果以异步方式获取任务处理状态及其结果(关键的重点是任务处理状态)则用Task。ValueTask是.NetCore2中才被内置定义的,所以ValueTask到目前为止还是新事物,所以要想更好的理解ValueTask必须先对Task(“https://www.cnblogs.com/wynblogscc/p/15138423.html”)、和Func参数有一些理解。
- Task的核心是“承诺”,在程序执行时,以Task进行定义的方法,开辟多个线程,CPU在固定的时间断内,以轮转的方式执行这些线程,以保证这些方法在逻辑上是同时开始的,并“承诺”(await)最终一定会获取任务所执行的状态和结果, 但是如果这些方法中有1个方法一定要获得其执行结果,则CPU在对该方法执行完成并获取其结果之前,不再以轮转的方式执行所有的线程, 只会执行该方法所在的线程, 这就是同时执行多个异步方法所造成“阻塞”现象的由来。
- 在工程性的Web 程序中,一般情况下会使用异步方法来定义控制器中的行为方法(async Task<IActionResult>),第1可以使用户可以获取最终的页面(“承诺”),以提供用户体验;第2由于大量用户的并发操作是常态化的应用场景,可以通过服务器端CUP轮转的方式执行这些线程,以保证大量用户并发操作时,服务器端依然能够把用户相获取的页面,在客户端的浏览器中渲染出来,这也是服务器CUP核心多内存很大的原因;这些应用现象对于GPU计算更为明显,GUP是以物理同时开始执行(不用轮转),同1种的操作,并获取操作的状态及其结果,这些结果反映上显示器上,所对应的显示器中的1小块区域,这也是GUP中的流处理器赿多,其速度赿快,刷新率越高的原因(同时开辟的线程赿多,所对应的显示器中的1小块区域赿小)。
- 在CUP以轮转的方式执行多个异步方法的线程时,如果必须获取1个方法的执行结果,如果使用ValueTask在获取结果,可以利用ValueTask的不“阻塞”性,依然可以让CPU以轮转的方式执行所有的线程,而不造成“阻塞”现象,ValueTask使CPU的轮转从任务方法对象(Task,可能包含结果)细粒度,细化到了任务结果(TResult)细粒度,这也是ValueTask实例会被作为参数实例的1个重要因素。
- 在工程性的Web 程序中,一般情况下会使用异步方法来定义控制器中的行为方法(async Task<IActionResult>),第1可以使用户可以获取最终的页面(“承诺”),以提供用户体验;第2由于大量用户的并发操作是常态化的应用场景,可以通过服务器端CUP轮转的方式执行这些线程,以保证大量用户并发操作时,服务器端依然能够把用户相获取的页面,在客户端的浏览器中渲染出来,这也是服务器CUP核心多内存很大的原因;这些应用现象对于GPU计算更为明显,GUP是以物理同时开始执行(不用轮转),同1种的操作,并获取操作的状态及其结果,这些结果反映上显示器上,所对应的显示器中的1小块区域,这也是GUP中的流处理器赿多,其速度赿快,刷新率越高的原因(同时开辟的线程赿多,所对应的显示器中的1小块区域赿小)。
- 在CUP以轮转的方式执行多个异步方法的线程时,如果必须获取1个方法的执行结果,如果使用ValueTask在获取结果,可以利用ValueTask的不“阻塞”性,依然可以让CPU以轮转的方式执行所有的线程,而不造成“阻塞”现象,ValueTask使CPU的轮转从任务方法对象(Task,可能包含结果)细粒度,细化到了任务结果(TResult)细粒度,这也是ValueTask实例会被作为参数实例的1个重要因素。
- 异步方法的本质是:CUP以轮转的方式,在逻辑上同时执行多个异步方法,所以调用异步方法的最好是异步方法,如果异步方法被同步方法调用,则会对异步方法实现的根本需要求造成冲突,因此该异步方法不能被CUP以轮转的方式进行执行了。如果异步方法中只调用同步方法而不调用异步方法,则该异步方法就没有被定义为异步方法的必要,在这种情况下该异步方法实质上是:以异步关键字定义的同步方法,这也是这种定义方式会出现语法警告的原因。
/// 【学生--类】
/// <remarks>
/// 摘要:
/// 学生实体类,通过该实体类及其属性成员实现当前程序与数据库中学生表之间的数据交互操作。
/// </remarks>
/// </summary>
public class Student
{
/// <summary>
/// 【编号】
/// <remarks>
/// 摘要:
/// 获取/设置1个指定学生的整型编号值。
/// </remarks>
/// </summary>
public int ID { get; set; }
/// <summary>
/// 【姓名】
/// <remarks>
/// 摘要:
/// 获取/设置1个指定学生的姓名。
/// </remarks>
/// </summary>
public string Name { get; set; }
/// <summary>
/// 【年龄】
/// <remarks>
/// 摘要:
/// 获取/设置1个指定学生年龄的整型值。
/// </remarks>
/// </summary>
public int Age { get; set; }
/// <summary>
/// 【性别】
/// <remarks>
/// 摘要:
/// 获取/设置1个指定学生的性别。
/// </remarks>
/// </summary>
public string Gender { get; set; }
}
/// <summary>
/// 【数据--类】
/// <remarks>
/// 摘要:
/// 实例化1/多个学生实例,并把这些实例存储到1个列表实例中。
/// </remarks>
/// </summary>
public class Data
{
/// <summary>
/// 【学生列表】
/// <remarks>
/// 摘要:
/// 获取/设置1个列表实例,该实例用于存储1/多个学生实例。
/// </remarks>
/// </summary>
public static List<Student> ListStudent { get; set; }
/// <summary>
/// 【获取列表】
/// <remarks>
/// 摘要:
/// 实例化1/多个学生实例,并把这些实例存储到1个列表实例中。
/// </remarks>
/// </summary>
public static List<Student> GetList()
{
ListStudent = new List<Student>();
for (int i = 0; i < 3; i++)
{
Student student = new Student()
{
ID = i,
Name = $"测试_{i}",
Age = 20,
Gender = "男"
};
ListStudent.Add(student);
}
return ListStudent;
}
}
public class ValueTaskDemo
{
/// <summary>
/// 【获取任务】
/// <remarks>
/// 摘要:
/// 以异步Task<int>方式获取1个整型值(关注的重点是返回结果)。
/// </remarks>
/// <returns>
/// 返回:
/// 1个指定类的实例。
/// </returns>
/// </summary>
public static Task<int> GetTask()
{
//由于返回的是TResult,而非Task<TResult>,所以该方法不能使用“async”关键字,否则会造成语法异常。
return Task.FromResult(1);
}
/// <summary>
/// 【获取值任务】
/// <remarks>
/// 摘要:
/// 以同步ValueTask<int>方式获取1个整型值(关注的重点是返回结果)。
/// </remarks>
/// <returns>
/// 返回:
/// 1个指定类的实例。
/// </returns>
/// </summary>
public static ValueTask<int> GetValueTask()
{
return new ValueTask<int>(1);
}
/// <summary>
/// 【异步获取值任务列表】
/// <remarks>
/// 摘要:
/// 以异步Task<List<Student>>方式获取1个列表实例(关注的重点是返回结果),该实例用于存储1/多个学生类的实例。
/// </remarks>
/// <returns>
/// 返回:
/// 1个列表实例,该实例用于存储1/多个学生类的实例。
/// </returns>
/// </summary>
public static async Task<List<Student>> GetTaskListAsync()
{
List<Student> list = new List<Student>();
list = await Task.Run<List<Student>>(() =>
{
return Data.GetList();
});
return list;
}
/// <summary>
/// 【获取值任务列表】
/// <remarks>
/// 摘要:
/// 以同步ValueTask<List<Student>>方式获取1个列表实例(关注的重点是返回结果),该实例用于存储1/多个学生类的实例。
/// </remarks>
/// <returns>
/// 返回:
/// 1个列表实例,该实例用于存储1/多个学生类的实例。
/// </returns>
/// </summary>
public static ValueTask<List<Student>> GetValueTaskList(List<Student> studentList)
{
return new ValueTask<List<Student>>(studentList);
}
}
public interface IRepository<T>
{
/// <typeparam name="T">泛型类型实例(1个指定类的类型实例)。</typeparam>
/// <param name="value">带2个参数的Func委托方法实例参数。</param>
/// <summary>
/// 【获取数据】
/// <remarks>
/// 摘要:
/// 通相应的参数,以同步ValueTask<T>方式获取1个指定类的实例。
/// </remarks>
/// <returns>
/// 返回:
/// 1个指定类的实例。
/// </returns>
/// </summary>
ValueTask<T> GetData(T value);
/// <typeparam name="T">泛型类型实例(1个指定类的类型实例)。</typeparam>
/// <param name="value">带2个参数的Func委托方法实例参数。</param>
/// <summary>
/// 【获取未完成数据】
/// <remarks>
/// 摘要:
/// 通相应的参数,以异步ValueTask<T>方式获取1个指定类的实例。
/// </remarks>
/// <returns>
/// 返回:
/// 1个指定类的实例。
/// </returns>
/// </summary>
ValueTask<T> GetDataIncompleteAsync(T value);
/// <typeparam name="T">泛型类型实例(1个指定类的类型实例)。</typeparam>
/// <param name="value">带2个参数的Func委托方法实例参数。</param>
/// <summary>
/// 【获取完成数据】
/// <remarks>
/// 摘要:
/// 通相应的参数,以异步ValueTask<T>方式获取1个指定类的实例。
/// </remarks>
/// <returns>
/// 返回:
/// 1个指定类的实例。
/// </returns>
/// </summary>
ValueTask<T> GetDataCompleteAsync(T value);
}
public class Repository<T> : IRepository<T>
{
/// <typeparam name="T">泛型类型实例(1个指定类的类型实例)。</typeparam>
/// <param name="value">带2个参数的Func委托方法实例参数。</param>
/// <summary>
/// 【获取数据】
/// <remarks>
/// 摘要:
/// 通相应的参数,以同步ValueTask<T>方式获取1个指定类的实例。
/// </remarks>
/// <returns>
/// 返回:
/// 1个指定类的实例。
/// </returns>
/// </summary>
public ValueTask<T> GetData(T value)
{
if (value == null)
value = default(T);
return new ValueTask<T>(value);
}
/// <typeparam name="T">泛型类型实例(1个指定类的类型实例)。</typeparam>
/// <param name="value">带2个参数的Func委托方法实例参数。</param>
/// <summary>
/// 【获取未完成数据】
/// <remarks>
/// 摘要:
/// 通相应的参数,以异步ValueTask<T>方式获取1个指定类的实例。
/// </remarks>
/// <returns>
/// 返回:
/// 1个指定类的实例。
/// </returns>
/// </summary>
public async ValueTask<T> GetDataIncompleteAsync(T value)
{
if (value == null)
value = default(T);
await Task.Delay(5000);//执行到await表达式时,立即返回到调用方法,等待5秒后执行后续部分。
return value;
}
/// <typeparam name="T">泛型类型实例(1个指定类的类型实例)。</typeparam>
/// <param name="value">带2个参数的Func委托方法实例参数。</param>
/// <summary>
/// 【获取完成数据】
/// <remarks>
/// 摘要:
/// 通相应的参数,以异步ValueTask<T>方式获取1个指定类的实例。
/// </remarks>
/// <returns>
/// 返回:
/// 1个指定类的实例。
/// </returns>
/// </summary>
public async ValueTask<T> GetDataCompleteAsync(T value)
{
if (value == null)
value = default(T);
//await Task.Delay(5000);//执行到await表达式时,立即返回到调用方法,等待5秒后执行后续部分。
await Task.CompletedTask;// 这里别误会,这是随便找个地方 await 一下。
return value;
}
}
WriteLine(ValueTaskDemo.GetTask());
WriteLine(ValueTaskDemo.GetTask().Result);
WriteLine(ValueTaskDemo.GetTask().IsCompleted);
WriteLine(ValueTaskDemo.GetValueTask());
WriteLine(ValueTaskDemo.GetValueTask().IsCompleted);
IRepository<int> repository = new Repository<int>();
ValueTask<int> result = repository.GetData(9);
if (result.IsCompleted)
{
WriteLine(result);
WriteLine("已经完成异步操作。");
}
else
{
WriteLine("未完成异步操作。");
}
ValueTask<int> resultIncompleteAsync = repository.GetDataIncompleteAsync(10);
if (resultIncompleteAsync.IsCompleted)
{
WriteLine(resultIncompleteAsync.Result);
WriteLine("resultIncompleteAsync:已经完成异步操作。");
}
else
{
WriteLine("resultIncompleteAsync:未完成异步操作。");
}
await resultIncompleteAsync;
WriteLine(resultIncompleteAsync.Result);
WriteLine($"resultIncompleteAsync:{resultIncompleteAsync.IsCompleted}。");
ValueTask<int> resultCompleteAsync = repository.GetDataCompleteAsync(11);
if (resultCompleteAsync.IsCompleted)
{
WriteLine(resultCompleteAsync.Result);
WriteLine("resultCompleteAsync:已经完成异步操作。");
}
else
{
WriteLine("resultCompleteAsync:未完成异步操作。");
}
WriteLine(resultCompleteAsync.Result);
WriteLine($"resultCompleteAsync:{resultCompleteAsync.IsCompleted}。");
foreach (var item in ValueTaskDemo.GetTaskListAsync().Result)
{
WriteLine(item.ID.ToString());
WriteLine(item.Name.ToString());
WriteLine(item.Gender.ToString());
WriteLine(item.Age.ToString());
}
foreach (var item in await ValueTaskDemo.GetValueTaskList(Data.GetList()))
{
WriteLine(item.ID.ToString());
WriteLine(item.Name.ToString());
WriteLine(item.Gender.ToString());
WriteLine(item.Age.ToString());
}
ReadKey();
对以上功能更为具体实现和注释见:22-05-28-050_ActionAndFunc参数_ValueTask(理解)。