Natasha 高级编译类 - 第二部分
Posted 摧残一生 涅槃重生
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Natasha 高级编译类 - 第二部分相关的知识,希望对你有一定的参考价值。
FakeMethodOperator
将以后的方法进行快速克隆,克隆时只会覆盖重新声明的信息,例如原来是public类型,不做定义的话,就直接拿过来使用。
好处:写好的模板不需要大变,变动的地方又代码进行修改
注意:使用Compile方法进行编译,参数为带入的参数,可通过调用.Compile().GetMethodInfo().Invoke()进行方法调用
-
需要克隆的类及用到的代理
///////////////需要使用的克隆类////////////////////// /// <summary> /// 等待克隆的类 /// </summary> public class OopTestModel /// <summary> /// 最基本的方法 /// </summary> public void ReWrite1() /// <summary> /// 并发执行的方法 /// </summary> /// <returns></returns> public async Task<OopTestModel> ReWrite2() return this; /// <summary> /// 虚方法,并带有ref和out /// </summary> /// <param name="i"></param> /// <param name="temp"></param> /// <returns></returns> public virtual ref int ReWrite3(ref int i, out string temp) temp = default!; return ref i; /// <summary> /// 虚方法声明的代理类 /// </summary> /// <param name="i"></param> /// <param name="temp"></param> /// <returns></returns> public delegate ref int TestDelegate1(ref int i, out string temp);
-
实现类
// 引用第一个方法,最简单的调用 var builder1 = FakeMethodOperator.DefaultDomain(); builder1 // 克隆OopTestModel的ReWrite1方法,加上!表示可以为null .UseMethod(typeof(OopTestModel).GetMethod("ReWrite1")!) // 方法体中加入下面方法 .MethodBody(@"Console.WriteLine(""hello world"");"); System.Console.WriteLine(builder1.ScriptCache); // 引用第二个方法,并发执行的方法 var builder2 = FakeMethodOperator.DefaultDomain() // 克隆OopTestModel的ReWrite2方法 .UseMethod(typeof(OopTestModel).GetMethod("ReWrite2")!) // 静态的方法体,注意,该处看不出是async的并发方法 .StaticMethodBody(@"Console.WriteLine(""hello world"");return default;"); if (builder2.Compile() != null) System.Console.WriteLine(builder2.ScriptCache); else System.Console.WriteLine("未编译成功"); //引用第三个,改为静态方法 var builder3 = FakeMethodOperator.DefaultDomain(); builder3 // 克隆OopTestModel的ReWrite3方法 .UseMethod(typeof(OopTestModel).GetMethod("ReWrite3")!) // 改为静态方法并设置方法体 .StaticMethodBody(@"temp = default;return ref i;"); //编译 加入代理 if (builder3.Compile<TestDelegate1>() != null) System.Console.WriteLine(builder3.ScriptCache); else System.Console.WriteLine("未编译成功");
运行结果
FastMethodOperator
用于快速构建方法并执行
注意:实例化的方法为Invoke,类似于反射
//最简单的快速调用
var delegateAction1 = FastMethodOperator
.DefaultDomain()
.Param<string>("str1")
.Param<string>("str2")
.Body(@"
string result = str1 +"" ""+ str2;
return result;")
.Return<string>()
// 编译
.Compile<Func<string, string, string>>();
// 执行
System.Console.WriteLine(delegateAction1.Invoke("Hello", "World1!"));
// delegateAction1的简化版,无需Param和Result
var delegateAction2 = FastMethodOperator.DefaultDomain()
.Body(@"return arg1 +"" ""+ arg2;")
.Compile<Func<string, string, string>>();
System.Console.WriteLine(delegateAction2.Invoke("Hello", "World2!"));
//并发执行的方法
// 注意执行该方法时一定要使用await或者Sleep,否则不会输出
var delegateAction3 = FastMethodOperator.DefaultDomain()
.Async()
.Body(@"
await Task.Delay(100);
string result = arg1 +"" ""+ arg2;
return result;")
.Compile<Func<string, string, Task<string>>>();
System.Console.Write
执行结果
NInterface
Natasha版本的接口快速构建
var builder = NInterface.DefaultDomain();
builder.AssemblyBuilder.DisableSemanticCheck();
var type = builder
.NoGlobalUsing()
.HiddenNamespace()
.Access(AccessFlags.Public)
.Name("Interface1")
.Property(item => item.Type<string>().Name("Abc"))
.Method(item => item.Name("Test").Param<string>("p").Return<int>())
.GetType();
System.Console.WriteLine(builder.AssemblyBuilder.SyntaxTrees[0].ToString());
执行结果
NInstance
直接实例化一个类NInstance.Creator
///待初始化类
public class CallModel
public CallModel()
Age = "World!";
CreateTime = DateTime.Now;
public string Age;
public DateTime CreateTime;
public String getCall()
return $"Age:CreateTime";
// 调用
var instance = NInstance.Creator<CallModel>();
var callModel = instance.Invoke();
System.Console.WriteLine(callModel.getCall());
执行结果
未完待续。。。
高级功能使用canvas元素(第二部分)
本文将继续介绍canvas的功能,展示如何绘制更复杂的图形(包括圆弧和曲线),如何使用剪裁区域来限制操作以及如何绘制文本。还是介绍可以应用在画布上的特效和变换,包括阴影、透明度、旋转和坐标重映射。
1. 用路径绘图
路径本质上是一组独立的线条(被称为子路径),它们组合到一起构成图形。我们绘制子路径的方式就像用笔在纸上画图一样,笔尖不离开纸面:画布上的每一条子路径都以上一条的终点作为起点。下面展示了绘制基本路径的方法:
绘制一条路径的基本顺序如下:
* 调用 beginPath方法;
* 用 moveTo方法移动到起点;
* 用 arc和 lineTo等方法绘制子路径;
* 调用 closePath方法(可选)
* 调用 fill或 stroke方法
1.1 用线条绘制路径
最简单的路径是那些由直线所组成的。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>由直线创建路径</title> <style> canvas {border: thin solid orange;} body > * {float: left;} </style> </head> <body> <canvas id="canvas" width="500" height="140"> Your browser doesn\'t support the <code>canvas</code> element </canvas> <script type="application/javascript"> var ctx = document.getElementById("canvas").getContext("2d"); ctx.fillStyle = "yellow"; ctx.strokeStyle = "black"; ctx.lineWidth = 4; ctx.beginPath(); ctx.moveTo(10,10); ctx.lineTo(110,10); ctx.lineTo(110,120); ctx.closePath(); ctx.fill(); ctx.beginPath(); ctx.moveTo(150,10); ctx.lineTo(200,10); ctx.lineTo(200,120); ctx.lineTo(190,120); ctx.fill(); ctx.stroke(); ctx.beginPath(); ctx.moveTo(250,10); ctx.lineTo(250,120); ctx.stroke(); </script> </body> </html>
此例创建了三条路径,其显示效果如下:
对于第一条路径。此例明确绘制了两条线,然后使用closePath方法,这样canvas就会闭合路径。然后调用了fill方法,用fillStyle属性所定义的样式填充了这个图形。
对于第二个图形,此例指定了三条子路径,然没有闭合图形。可以看到调用了fill和stroke这两个方法来给图形填色,并沿着路径绘制了一条线。请注意填充色的绘制方式就行图形已经闭合了那样。canvas元素会假定存在一条从终点到起点的子路径,然后借助它填充图形。相比之下,stroke方法只会沿着已经定义的子路径。
PS:对于第二个图形,在stroke方法之前先调用了fill方法,这会使canvas先用纯色填充图形,然后再沿着路径绘制线条。如果lineWidth属性大于1并且先调用了stroke方法,得到的视觉效果就会有所不同。更宽的线条会在路径两侧绘制。因此线条的一部分会在调用fill方法时被遮盖,导致线条的宽度变窄。
对于第三个图形,此例只是简单地在两点之间绘制了一条线,因为路径不一定需要包含多条子路径。我们在绘制线条或未闭合图形时可以使用 lineCap属性来设置线条末端的样式。这个属性允许的三个值是:butt、round 和square(butt为默认值)。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>设置lineCap属性</title> <style> canvas {border: thin solid orange;} body > * {float: left;} </style> </head> <body> <canvas id="canvas" width="500" height="140"> Your browser doesn\'t support the <code>canvas</code> element </canvas> <script type="application/javascript"> var ctx = document.getElementById("canvas").getContext("2d"); ctx.strokeStyle = "red"; ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(0,50); ctx.lineTo(200,50); ctx.stroke(); ctx.strokeStyle = "black"; ctx.lineWidth = 40; var xpos = 50; var styles = ["butt","round","square"]; for(var i=0;i<styles.length;i++){ ctx.beginPath(); ctx.lineCap = styles[i]; ctx.moveTo(xpos,50); ctx.lineTo(xpos,150); ctx.stroke(); xpos += 50; } </script> </body> </html>
此例的脚本为每一种样式都绘制了一条非常粗的线。默认值作为参考线,用它演示 round和 square样式在绘制时会超过线条末端。
1.2 绘制矩形
rect 方法会给当前路径添加一条矩形的子路径。如果只需要一个单独的矩阵,那么之前介绍的 fillRect和 strokeRect方法是更合适的选择。如果需要给一个更复杂的图像添加矩形,rect方法就很有用了。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>用rect方法绘制矩形</title> <style> canvas {border: thin solid orange;} body > * {float: left;} </style> </head> <body> <canvas id="canvas" width="500" height="140"> Your browser doesn\'t support the <code>canvas</code> element </canvas> <script type="application/javascript"> var ctx = document.getElementById("canvas").getContext("2d");
ctx.fillStyle = "yellow"; ctx.strokeStyle = "red"; ctx.lineWidth = 4; ctx.beginPath(); ctx.moveTo(110,10); ctx.lineTo(110,100); ctx.lineTo(10,10); ctx.closePath(); ctx.rect(110,10,100,90); ctx.rect(110,100,130,30); ctx.fill(); ctx.stroke(); </script> </body> </html>
使用rect方法时不需要moveTo方法,因为我们在此方法的前两个参数里已经指定了矩形的坐标。此例绘制了一对线条,调用closePath方法创建了一个三角形,然后绘制了两个邻接的矩形,此例显示效果如下:
子路径不一定需要相连才能组成路径。可以绘制几条分离的子路径,它们仍然会被视为同一个图形的组成部分。修改JavaScript代码如下:
<script type="application/javascript"> var ctx = document.getElementById("canvas").getContext("2d"); ctx.fillStyle = "yellow"; ctx.strokeStyle = "red"; ctx.lineWidth = 4; ctx.beginPath(); ctx.moveTo(110,10); ctx.lineTo(110,100); ctx.lineTo(10,10); ctx.closePath(); ctx.rect(120,10,100,90); ctx.rect(150,110,130,20); ctx.fill(); ctx.stroke(); </script>
此例里的子路径之间并不相连,但总体结果仍然是一条单独的路径。当调用 stroke或 fill方法时,它们的效果会应用到所有创建的子路径上,其显示效果如下:
2. 绘制圆弧
我们使用 arc和 arcTo方法在画布上绘制圆弧,不过这两种方法绘制圆弧的方式有所不同。下表介绍了canvas里与圆弧有关的方法:
2.1 使用arcTo方法
下例演示了如何使用arcTo方法:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>使用arcTo方法</title> <style> canvas {border: thin solid orange;} body > * {float: left;} </style> </head> <body> <canvas id="canvas" width="500" height="140"> Your browser doesn\'t support the <code>canvas</code> element </canvas> <script type="application/javascript"> var ctx = document.getElementById("canvas").getContext("2d"); var point1 = [100,10]; var point2 = [200,10]; var point3 = [200,110]; ctx.fillStyle = "yellow"; ctx.strokeStyle = "blue"; ctx.lineWidth = 4; ctx.beginPath(); ctx.moveTo(point1[0],point1[1]); ctx.arcTo(point2[0],point2[1],point3[0],point3[1],100); ctx.stroke(); drawPoint(point1[0],point1[1]); drawPoint(point2[0],point2[1]); drawPoint(point3[0],point3[1]); ctx.beginPath(); ctx.moveTo(point1[0],point1[1]); ctx.lineTo(point2[0],point2[1]); ctx.lineTo(point3[0],point3[1]); ctx.stroke(); function drawPoint(x,y){ ctx.lineWidth = 1; ctx.strokeStyle = "red"; ctx.strokeRect(x-2,y-2,4,4); } </script> </body> </html>
arcTo方法绘制的圆弧依靠两条线完成。第一条是从上一条子路径的终点绘制到前两个方法参数所描述的点。第二条线是从前两个方法参数所描述的点绘制到第三个和第四个参数所描述的点。然后canvas会绘制从上一条子路径的终点到第二个点之间最短的一条圆弧,其半径由最后一个参数指定。为了让它便于理解,此例给画布添加了两条额外的路径来提供一些上下文信息,此例显示效果如下:
可以看到两条红色的线。此例指定了一个半径,两条线的长度也完全一致,这就意味着我们得到一条匀称的曲线,刚好触碰到上一条子路径的终点和第三个与第四个参数所描述的点。半径和线条长度并不总是具有如此方便的尺寸,所以canvas会根据需要调整所绘制的圆弧。演示例子如下,此例使用事件监控鼠标运动,在屏幕上的鼠标移动时为不同的点绘制圆弧:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>响应鼠标绘制移动绘制圆弧</title> <style> canvas {border: thin solid orange;} body > * {float: left;} </style> </head> <body> <canvas id="canvas" width="500" height="140"> Your browser doesn\'t support the <code>canvas</code> element </canvas> <script type="application/javascript"> var canvasElement = document.getElementById("canvas"); var ctx = document.getElementById("canvas").getContext("2d"); var point1 = [100,10]; var point2 = [200,10]; var point3 = [200,110]; draw(); canvasElement.onmousemove = function(e){ if(e.ctrlKey){ point1 = [e.clientX, e.clientY]; }else if(e.shiftKey){ point2 = [e.clientX, e.clientY]; }else { point3 = [e.clientX, e.clientY]; } ctx.clearRect(0,0,500,140); draw(); } function draw(){ ctx.以上是关于Natasha 高级编译类 - 第二部分的主要内容,如果未能解决你的问题,请参考以下文章
阶段1 语言基础+高级_1-3-Java语言高级_03-常用API第二部分_第6节 基本类型包装类_4_包装类_基本类型与字符串类型之间
阶段1 语言基础+高级_1-3-Java语言高级_03-常用API第二部分_第3节 Calendar类_2_Calendar类的常用成员方法