当前位置: 首页 > news >正文

罗湖附近公司做网站建设哪家服务周到开网站建设

罗湖附近公司做网站建设哪家服务周到,开网站建设,亿藤互联网站建设开发,关键词排名关键词优化距离上一篇文章已经过去了挺久的,很长时间没有写GH基础部分的内容了,原因其一是本职工作太忙了,进度也有些落后,白天工作累成马,回家只想躺着;其二则是感觉GH基础系列基本上也介绍得差不多了,电…

距离上一篇文章已经过去了挺久的,很长时间没有写GH基础部分的内容了,原因其一是本职工作太忙了,进度也有些落后,白天工作累成马,回家只想躺着;其二则是感觉GH基础系列基本上也介绍得差不多了,电池二次开发的一些基本操作(功能/外观)都介绍得差不多了,再加上前几期写的数据类型,这基本上就囊括了所有二次开发需要用到的内容。

不过,理论知识和实践总归是有一些差距的,在CSDN上还是会偶尔收到私信问一些细节问题的二开爱好者们。这些问题确实是做电池二次开发的时候遇到的,但它们本身可能与电池的二次开发没有关系:其中有一部分是C#代码本身的编程逻辑问题,还有一部分是有关于Rhino的SDK的问题,另外还有一些关于Windows Form、WPF等前端框架的问题。有些问题会被反复地问到,所以笔者决定还是多多将大家遇到的有共性的问题也做一系列解答,方便读者在还没有遇到这些类似的问题的时候,能够有那么一点点印象,当真正碰到这些问题的时候,能够找对解决问题的方向,少走一些弯路。

这篇文章要讲的问题是有关于右键菜单的菜单项的回调函数的问题,这个问题的根源是来自
C#代码编程本身,也是十分具有迷惑性,相信没有完整看过C#基础知识直接上手二开的爱好者们在第一次遇到这个问题的时候肯定十分地困惑。下面就来看具体问题吧。

近期经常收到一个问题 —— “为什么我添加的右键菜单项有Bug?” “我用了一个for循环去添加菜单项,想一次性添加x个菜单项,并在菜单被点击的时候执行 xxxx,但是结果总是不变,而且不对,这是不是GH出Bug了?”

相信有不少二开的小伙伴会做这样的一个需求:需要一个电池,这个电池需要依照情况输出若干个确定的值,具体输出哪个值需要用右键菜单来指定。类似于 ValueList 电池那样可以通过选择来输出若干个指定值其中的一个。

Snipaste_2023-08-29_15-18-27

Snipaste_2023-08-29_15-30-28

要实现这个功能,最简单直观的就是在电池中加入一个属性叫 ComponentPropertyValue,然后在右键菜单中改变它,并调用 ExpireSolution,同时,SolveInstance 函数中依照这个属性来赋值:

private int ComponentPropertyValue { get; set; }protected override void AppendAdditionalComponentMenuItems(ToolStripDropDown menu)
{menu.Items.Add(new ToolStripMenuItem("1", null, (o, e) => { ComponentPropertyValue = 1; this.ExpireSolution(true); }));menu.Items.Add(new ToolStripMenuItem("2", null, (o, e) => { ComponentPropertyValue = 2; this.ExpireSolution(true); }));menu.Items.Add(new ToolStripMenuItem("3", null, (o, e) => { ComponentPropertyValue = 3; this.ExpireSolution(true); }));menu.Items.Add(new ToolStripMenuItem("4", null, (o, e) => { ComponentPropertyValue = 4; this.ExpireSolution(true); }));menu.Items.Add(new ToolStripMenuItem("5", null, (o, e) => { ComponentPropertyValue = 5; this.ExpireSolution(true); }));
}protected override void SolveInstance(IGH_DataAccess DA)
{// 这里为了举例方便设置为该数值的平方// 实际可能会有较为复杂的运算逻辑DA.SetData(0, ComponentPropertyValue * ComponentPropertyValue);
}

显然,作为一个写过一段时间代码的正常人,应该能想到使用一个 for 循环来改写函数 AppendAdditionalComponentMenuItems 中的代码:

protected override void AppendAdditionalComponentMenuItems(ToolStripDropDown menu)
{for (var i = 1; i < 6; i++){// 将对应的列表项的文字和赋值语句换成 i 即可menu.Items.Add(new ToolStripMenuItem($"{i}", null, (o, e) => { ComponentPropertyValue = i; this.ExpireSolution(true); }));}
}

但是这个时候运行代码就会出现一个现象,无论选哪个,最后出来的结果都会是36。

Rhino_6mowQGoDy5

?????

“这GH是出Bug了!”

其实不然,即便是一个控制台应用程序,下面这段代码也会只输出一个值:

static void Main() 
{var list = new List<Action>();for (var x = 0; x < 10; x++){list.Add(() => Console.WriteLine(x));}foreach (var action in list){action();}
}

Snipaste_2023-08-29_16-09-17

甚至,在广为人知的另一门编程语言 Python 中,以及其他许多编程语言中,都会有这种情况。(在 Python 中,这种现象称之为“闭包延时绑定”,可自行搜索Python延时绑定关键词来查询相关底层知识)

我们先说怎么解决这个问题,再来谈这个问题是什么原因导致的。


如何解决

解决的方法很简单,只需要额外增加一个局部变量即可:

protected override void AppendAdditionalComponentMenuItems(ToolStripDropDown menu)
{for (var i = 1; i < 6; i++){var j = i; // 增加一个额外的变量j,令其值等于i,然后在lambda函数中使用j即可menu.Items.Add(new ToolStripMenuItem($"{j}", null,(o, e) => { ComponentPropertyValue = j; this.ExpireSolution(true); }));}
}

简而言之,就是在 for 循环内部作用域,创建一个额外的临时变量(上例中的j),令其等于循环控制变量(上例中的i),然后在循环内部作用域使用这个额外的临时变量即可。

笔者提示:此外,如果循环控制变量(上例中的i)是引用类型(不是int/double/long等值类型),这个循环内部的额外临时变量则需要使用复制构造来创建新实例 —— 虽然很少出现使用非 int 类型作为循环控制变量

这样一来,这个电池的工作就正常了:

Rhino_pbpZsxoWW4

为什么会是这样的

细心的读者已经发现了,在上面的例子中,我们都使用了 匿名函数。没错,问题就是出在 匿名函数 中。

匿名函数写起来十分方便,但其实在它简单的语法背后,编译器为我们做了许多额外的事情。其中之一就是对其中的变量做 “变量捕获 (Captures)”。

变量捕获描述的是这样一个过程:

对于匿名函数的函数体中使用到的不存在于函数输入参数的变量,匿名函数会捕获该变量的引用。在随后匿名函数被调用时,被捕获的变量的值将会是函数调用这一瞬间的值,而非匿名函数构造时的值。

上面两句话阐述了两个问题:

  • 什么样的变量会被捕获
  • 被捕获变量的行为是什么

下面看一个例子:

var x = 10;
Func<int, int> lambda = (int input) => input * x;
x += 10;
var result = lambda(5);
Console.WriteLine(result);

我们使用 Visual Studio 中的 C# Interactive 来执行上面的代码,可以看到,lambda(5) 的结果是100,而不是50。

Snipaste_2023-08-29_16-38-22

  • 匿名函数是: (int input) => input * x
  • 匿名函数的输入变量是 input
  • 匿名函数体是 input * x

匿名函数体中包含了两个变量,inputx。因为input是匿名函数的输入变量,所以它不是被捕获的变量。x不是匿名函数的输入变量,所以它将会被匿名函数捕获。

在我们使用lambda(5)调用匿名函数时,被捕获变量x的值是匿名函数函数调用时的值(20,因为在调用前我们使用x += 10改变了x),而非匿名函数被定义的时候的值(10)。因此,最后的结果是 5 * 20 = 100

通过这个例子,我们可以看出:

匿名函数中的被捕获的变量的值会是匿名函数被调用时的值,而非匿名函数构造时的值。

因此,在的Grasshopper电池菜单项的问题上,我们构造菜单项时,是嵌套在 for 循环中,构造匿名函数时,由于循环变量i并不是匿名函数的输入参数,所以它将会被捕获!我们通过 for 循环构造了5个菜单项,但他们的回调函数捕获的是同一个循环变量 i

protected override void AppendAdditionalComponentMenuItems(ToolStripDropDown menu)
{for (var i = 1; i < 6; i++){menu.Items.Add(new ToolStripMenuItem($"{i}", null, (o, e) => { ComponentPropertyValue = i; this.ExpireSolution(true); }));}
}

进一步的,在菜单被点击的时候,回调函数被触发,此时匿名函数内的i的值会是匿名函数被调用时候的值(此时,构造菜单项的 for 循环早已完成,因此循环变量停留在了最后一次 for循环的值6)。这也是为什么我们在之前出现,任何一个菜单项点击都是6的结果的原因。

老规矩,上代码

using System;
using System.Windows.Forms;using Grasshopper.Kernel;namespace GrasshopperPluginExample01
{public class ProvideValues : GH_Component{public ProvideValues() : base("ProvideValues", "Val","ProvideValues","Params", "DigitalCrab"){}private int ComponentPropertyValue;protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager) { }protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager){pManager.AddIntegerParameter("Out", "O", "output value", GH_ParamAccess.item);}protected override void SolveInstance(IGH_DataAccess DA){DA.SetData(0, ComponentPropertyValue * ComponentPropertyValue);}protected override void AppendAdditionalComponentMenuItems(ToolStripDropDown menu){//menu.Items.Add(new ToolStripMenuItem("1", null, (o, e) => { ComponentPropertyValue = 1; this.ExpireSolution(true); }));//menu.Items.Add(new ToolStripMenuItem("2", null, (o, e) => { ComponentPropertyValue = 2; this.ExpireSolution(true); }));//menu.Items.Add(new ToolStripMenuItem("3", null, (o, e) => { ComponentPropertyValue = 3; this.ExpireSolution(true); }));//menu.Items.Add(new ToolStripMenuItem("4", null, (o, e) => { ComponentPropertyValue = 4; this.ExpireSolution(true); }));//menu.Items.Add(new ToolStripMenuItem("5", null, (o, e) => { ComponentPropertyValue = 5; this.ExpireSolution(true); }));for (var i = 1; i < 6; ++i){var j = i;menu.Items.Add(new ToolStripMenuItem($"{j}", null, (o, e) => { ComponentPropertyValue = j; this.ExpireSolution(true); }));}}protected override System.Drawing.Bitmap Icon => null;public override Guid ComponentGuid => new("7805627F-6422-457D-969D-C5E19B124D87");}
}

下次再见 🦀

http://www.yayakq.cn/news/769088/

相关文章:

  • 珠海企业建站模板江苏建设考试网官网
  • 计算机网络 网站开发与设计中文安卓开发工具
  • 建设网站的规则wordpress笑话类模板
  • 做电影网站挣钱西安推广公司无网不胜
  • 西安做网站的公司维护wordpress 七牛加速
  • ui设计参考网站怎么把网站管理系统
  • 百度云服务器做php网站如何推广自己的产品让更多人来买
  • wordpress 移动站插件电脑培训零基础培训班
  • 顺德做网站设计的公司wordpress配置数据库连接
  • 做出口网站什么专业的会做网站
  • 做策划的都上哪些网站搜索资料间备案期间网站可以做竞价吗
  • 长沙专业网站建设服务中铁建设集团有限公司总部在哪
  • 浙江省和住房建设厅网站网站优化的基本思想与原则
  • 寻找邯郸网站建设旅游网站模板文章
  • dede模板蓝色大气简洁企业网站模板微信公众号(网站建设)合同
  • 青海公司网站建设哪家好简易网页一键生成
  • 免费可以绑定域名网站空间广西智能网站建设哪家有
  • 做评选活动的网站17做网店网站池尾
  • 网站建设企业所得税wordpress邮件功能用不了
  • 企业网站建设实训报告先做网站后台还是前台
  • 重庆专业做网站网站建设项目汇报
  • 网站的大图传不上去是怎么回事重庆景点门票价格一览表
  • 泉州响应式网站建设销售类网站数据库的建设
  • 上线了做网站怎么样青岛网页制作案例
  • 安徽网新科技网站建设介绍开网站建设
  • win7 asp网站无法显示该页面网络策划书一般包括哪些内容
  • 做数码测评的网站wordpress文章商品导购
  • 快速建站教程网网站基本代码
  • 友情链接如何选择网站wordpress如何修改背景图片
  • 石家庄外贸做网站seo是什么职业