public delegate void Method ();
public class buttonData
{
public string name;
public Method method;
}
private void Update ()
{
if (Input.GetKeyDown(KeyCode.Q))
{
List<buttonData> list2 = new List<buttonData>();
buttonData button = new buttonData();
button.name = "abc";
button.method = new Method(Test);
list2.Add(button);
OfferUIComponent(list2)
}
}
public void OfferUIComponent ( List<buttonData> button )
{
for (int i = 0; i < button.Count; i++)
{
GameObject buttonObj = Instantiate(Resources.Load("Prefabs/tooltipButton") as GameObject);
buttonObj.name = button[i].name;
if (null != button[i].method)
{
buttonObj.GetComponent<Button>().onClick.AddListener(delegate { button[i].method(); });
}
}
}
当我在下面的for循环中添加事件时,直接用button[i].method()就会报超出界限的错误,然而如果我用int a=i;存一下i的值,添加的时候用button[a].method则可以正常调用,请大神们讲解一下其中的原理呗。并且上面的name赋值用i是可以正常使用的。
4 个解决方案
#1
内层的函数可以引用包含在它外层的函数的变量,即使外层函数的执行已经终止。但该变量提供的值并非变量创建时的值,而是在父函数范围内的最终值。
这个问题,可以搜一下C# 闭包
这个问题,可以搜一下C# 闭包
#2
闭包问题
和初学者 判断 值类型,引用类型 的区别差不多
通俗来讲 就是delegate中的匿名函数中运用的是外层的变量引用 并不是值
所以当你第一次循环的时候记录的是i也就是目前0 也就是局部变量i的地址
下一次循环的时候 i 变成了 1 地址还是这个地址 所以上一次所引用的值就变成了现在的1
以此类推 剩下的i都是这个原理 最终匿名函数中每个i值存的都是最后的这个i值
而你之后用int a = i 存起来了 每次循环都是新的局部变量
所以地址当然不一样 所以循环之后 匿名函数中存的都是正确的a值 也就是i值
和初学者 判断 值类型,引用类型 的区别差不多
通俗来讲 就是delegate中的匿名函数中运用的是外层的变量引用 并不是值
所以当你第一次循环的时候记录的是i也就是目前0 也就是局部变量i的地址
下一次循环的时候 i 变成了 1 地址还是这个地址 所以上一次所引用的值就变成了现在的1
以此类推 剩下的i都是这个原理 最终匿名函数中每个i值存的都是最后的这个i值
而你之后用int a = i 存起来了 每次循环都是新的局部变量
所以地址当然不一样 所以循环之后 匿名函数中存的都是正确的a值 也就是i值
#3
如3楼所说的,这个闭包问题只有是在局部变量的时候产生吗?发帖人用一个int a=i;这个int a;也属于局部变量不是么?那为什么不会产生a的地址不变,从上 一级函数继承下来呢,也就是i一直记录着前一次调用i之后的i+1,而不是从i=0开始,那么a也理应因为这个“闭包问题”而产生同样的效果才对。
4楼最后一段说: 而你之后用int a = i 存起来了 每次循环都是新的局部变量
所以地址当然不一样 所以循环之后 匿名函数中存的都是正确的a值 也就是i值
我仔细想了一下这一段话,是不是说多个匿名函数如果产生了闭包问题,那么它们会捆绑在一起,使用同一个地址的变量,而因为int a产生新的地址空间,才不会发生这种闭包的事情。。但是看看题主所说的问题,是报超出界限的错误,然而如果我用int a=i;存一下i的值,添加的时候用button[a].method则可以正常调用,没有说到是delegate错误呀!难道delegate里面有for循环???其实我什么都不懂只是没事无聊发一下言打发时间,不喜勿喷
4楼最后一段说: 而你之后用int a = i 存起来了 每次循环都是新的局部变量
所以地址当然不一样 所以循环之后 匿名函数中存的都是正确的a值 也就是i值
我仔细想了一下这一段话,是不是说多个匿名函数如果产生了闭包问题,那么它们会捆绑在一起,使用同一个地址的变量,而因为int a产生新的地址空间,才不会发生这种闭包的事情。。但是看看题主所说的问题,是报超出界限的错误,然而如果我用int a=i;存一下i的值,添加的时候用button[a].method则可以正常调用,没有说到是delegate错误呀!难道delegate里面有for循环???其实我什么都不懂只是没事无聊发一下言打发时间,不喜勿喷
#4
如3楼所说的,这个闭包问题只有是在局部变量的时候产生吗?发帖人用一个int a=i;这个int a;也属于局部变量不是么?那为什么不会产生a的地址不变,从上 一级函数继承下来呢,也就是i一直记录着前一次调用i之后的i+1,而不是从i=0开始,那么a也理应因为这个“闭包问题”而产生同样的效果才对。这一段话我收回
#1
内层的函数可以引用包含在它外层的函数的变量,即使外层函数的执行已经终止。但该变量提供的值并非变量创建时的值,而是在父函数范围内的最终值。
这个问题,可以搜一下C# 闭包
这个问题,可以搜一下C# 闭包
#2
闭包问题
和初学者 判断 值类型,引用类型 的区别差不多
通俗来讲 就是delegate中的匿名函数中运用的是外层的变量引用 并不是值
所以当你第一次循环的时候记录的是i也就是目前0 也就是局部变量i的地址
下一次循环的时候 i 变成了 1 地址还是这个地址 所以上一次所引用的值就变成了现在的1
以此类推 剩下的i都是这个原理 最终匿名函数中每个i值存的都是最后的这个i值
而你之后用int a = i 存起来了 每次循环都是新的局部变量
所以地址当然不一样 所以循环之后 匿名函数中存的都是正确的a值 也就是i值
和初学者 判断 值类型,引用类型 的区别差不多
通俗来讲 就是delegate中的匿名函数中运用的是外层的变量引用 并不是值
所以当你第一次循环的时候记录的是i也就是目前0 也就是局部变量i的地址
下一次循环的时候 i 变成了 1 地址还是这个地址 所以上一次所引用的值就变成了现在的1
以此类推 剩下的i都是这个原理 最终匿名函数中每个i值存的都是最后的这个i值
而你之后用int a = i 存起来了 每次循环都是新的局部变量
所以地址当然不一样 所以循环之后 匿名函数中存的都是正确的a值 也就是i值
#3
如3楼所说的,这个闭包问题只有是在局部变量的时候产生吗?发帖人用一个int a=i;这个int a;也属于局部变量不是么?那为什么不会产生a的地址不变,从上 一级函数继承下来呢,也就是i一直记录着前一次调用i之后的i+1,而不是从i=0开始,那么a也理应因为这个“闭包问题”而产生同样的效果才对。
4楼最后一段说: 而你之后用int a = i 存起来了 每次循环都是新的局部变量
所以地址当然不一样 所以循环之后 匿名函数中存的都是正确的a值 也就是i值
我仔细想了一下这一段话,是不是说多个匿名函数如果产生了闭包问题,那么它们会捆绑在一起,使用同一个地址的变量,而因为int a产生新的地址空间,才不会发生这种闭包的事情。。但是看看题主所说的问题,是报超出界限的错误,然而如果我用int a=i;存一下i的值,添加的时候用button[a].method则可以正常调用,没有说到是delegate错误呀!难道delegate里面有for循环???其实我什么都不懂只是没事无聊发一下言打发时间,不喜勿喷
4楼最后一段说: 而你之后用int a = i 存起来了 每次循环都是新的局部变量
所以地址当然不一样 所以循环之后 匿名函数中存的都是正确的a值 也就是i值
我仔细想了一下这一段话,是不是说多个匿名函数如果产生了闭包问题,那么它们会捆绑在一起,使用同一个地址的变量,而因为int a产生新的地址空间,才不会发生这种闭包的事情。。但是看看题主所说的问题,是报超出界限的错误,然而如果我用int a=i;存一下i的值,添加的时候用button[a].method则可以正常调用,没有说到是delegate错误呀!难道delegate里面有for循环???其实我什么都不懂只是没事无聊发一下言打发时间,不喜勿喷
#4
如3楼所说的,这个闭包问题只有是在局部变量的时候产生吗?发帖人用一个int a=i;这个int a;也属于局部变量不是么?那为什么不会产生a的地址不变,从上 一级函数继承下来呢,也就是i一直记录着前一次调用i之后的i+1,而不是从i=0开始,那么a也理应因为这个“闭包问题”而产生同样的效果才对。这一段话我收回