- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
在下面的方法中,我发送了一个 Action 枚举,并希望返回一个调用 Action<object>
的 ICommand 数组。包装那些 Action (relayCommand 需要)。
问题是,如果我在 for each(甚至是 for 循环)中执行此操作,我得到的命令总是执行参数中传递的第一个操作。
public static ICommand[] CreateCommands(IEnumerable<Action> actions)
{
List<ICommand> commands = new List<ICommand>();
Action[] actionArray = actions.ToArray();
// works
//commands.Add(new RelayCommand(o => { actionArray[0](); })); // (_execute = {Method = {Void <CreateCommands>b__0(System.Object)}})
//commands.Add(new RelayCommand(o => { actionArray[1](); })); // (_execute = {Method = {Void <CreateCommands>b__1(System.Object)}})
foreach (var action in actionArray)
{
// always add the same _execute member for each RelayCommand (_execute = {Method = {Void <CreateCommands>b__0(System.Object)}})
commands.Add(new RelayCommand(o => { action(); }));
}
return commands.ToArray();
}
似乎 lambda 总是在循环内重用,认为它做的是一样的,但事实并非如此。
我该如何克服这种情况?我如何强制循环威胁 o => { action(); }
总是像新的一样?
谢谢!
我按照建议尝试但没有帮助:
foreach (var action in actionArray)
{
Action<object> executeHandler = o => { action(); };
commands.Add(new RelayCommand(executeHandler));
}
似乎对我有用的是:
class RelayExecuteWrapper
{
Action _action;
public RelayExecuteWrapper(Action action)
{
_action = action;
}
public void Execute(object o)
{
_action();
}
}
/// ...
foreach (var action in actionArray)
{
RelayExecuteWrapper rxw = new RelayExecuteWrapper(action);
commands.Add(new RelayCommand(rxw.Execute));
}
RelayCommand 代码:
/// <summary>
/// A command whose sole purpose is to
/// relay its functionality to other
/// objects by invoking delegates. The
/// default return value for the CanExecute
/// method is 'true'.
/// </summary>
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
/// <summary>
/// Creates a new command that can always execute.
/// </summary>
/// <param name="execute">The execution logic.</param>
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
/// <summary>
/// Creates a new command.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution status logic.</param>
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute(parameter);
}
#endregion // ICommand Members
}
最佳答案
这个问题每周都会在 StackOverflow 上报告几次。问题是在循环内创建的每个新 lambda 共享相同“ Action ”变量。 lambda 不捕获值,它们捕获变量。也就是说,当你说
List<Action> list = new List<Action>();
foreach(int x in Range(0, 10))
list.Add( ()=>{Console.WriteLine(x);} );
list[0]();
那当然会打印“10”,因为这是 x now 的值。该操作是“写入 x 的当前值”,而不是“写入创建委托(delegate)时 x 返回的值”。
为了解决这个问题,创建一个新变量:
List<Action> list = new List<Action>();
foreach(int x in Range(0, 10))
{
int y = x;
list.Add( ()=>{Console.WriteLine(y);} );
}
list[0]();
由于这个问题很常见,我们正在考虑更改 C# 的下一个版本,以便每次通过 foreach 循环都会创建一个新变量。
参见 http://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/了解更多详情。
更新:来自评论:
Every ICommand has the same methodinfo:
{ Method = {Void <CreateCommands>b__0(System.Object)}}
是的,当然可以。方法每次都一样。我认为您误解了委托(delegate)创建是什么。这样看。假设你说:
var firstList = new List<Func<int>>()
{
()=>10, ()=>20
};
好的,我们有一个返回整数的函数列表。第一个返回 10,第二个返回 20。
这与:
static int ReturnTen() { return 10; }
static int ReturnTwenty() { return 20; }
...
var firstList = new List<Func<int>>()
{ ReturnTen, ReturnTwenty };
到目前为止有意义吗?现在我们添加您的 foreach 循环:
var secondList = new List<Func<int>>();
foreach(var func in firstList)
secondList.Add(()=>func());
好的,那是什么意思?这意味着完全相同的事情:
class Closure
{
public Func<int> func;
public int DoTheThing() { return this.func(); }
}
...
var secondList = new List<Func<int>>();
Closure closure = new Closure();
foreach(var func in firstList)
{
closure.func = func;
secondList.Add(closure.DoTheThing);
}
现在清楚这里发生了什么了吗?每次循环你都不会创建一个新的闭包,你当然不会创建一个新的方法。您创建的委托(delegate)始终指向相同的方法,并且始终指向相同的闭包。
现在,如果你写成
foreach(var loopFunc in firstList)
{
var func = loopFunc;
secondList.Add(func);
}
那么我们生成的代码将是
foreach(var loopFunc in firstList)
{
var closure = new Closure();
closure.func = loopFunc;
secondList.Add(closure.DoTheThing);
}
现在列表中的每个新函数都有相同的方法信息——它仍然是 DoTheThing——但有一个不同的闭包。
现在您看到结果的原因有意义吗?
您可能还想阅读:
What is the lifetime of a delegate created by a lambda in C#?
另一个更新:来自已编辑的问题:
What I tried as per suggestions, but did not help:
foreach (var action in actionArray)
{
Action<object> executeHandler = o => { action(); };
commands.Add(new RelayCommand(executeHandler)); }
}
当然这没有帮助。这与以前有完全相同的问题。 问题是 lambda 在单个变量“action”上封闭,而不是在每个 action 值上封闭。在创建 lambda 的地方四处移动显然不能解决这个问题。您要做的是创建一个新变量。您的第二个解决方案通过创建一个引用类型的字段来分配一个新变量。您不需要明确地这样做;正如我上面提到的,如果您在循环体内部创建一个新变量,编译器会为您这样做。
解决这个问题的正确而简便的方法是
foreach (var action in actionArray)
{
Action<object> copy = action;
commands.Add(new RelayCommand(x=>{copy();}));
}
这样您每次通过循环时都会创建一个新变量,因此循环中的每个 lambda 都会关闭一个不同的变量。
每个委托(delegate)都有相同的方法信息但不同的闭包。
I'm not really sure about these closure and lambdas
您正在您的程序中进行高阶函数式编程。 如果您想有任何机会正确地这样做,您最好了解“这些闭包和 lambda”。现在是时候了。
关于c# - 循环优化或 lambda 闭合有问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6360637/
#include using namespace std; class C{ private: int value; public: C(){ value = 0;
这个问题已经有答案了: What is the difference between char a[] = ?string?; and char *p = ?string?;? (8 个回答) 已关闭
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 7 年前。 此帖子已于 8 个月
除了调试之外,是否有任何针对 c、c++ 或 c# 的测试工具,其工作原理类似于将独立函数复制粘贴到某个文本框,然后在其他文本框中输入参数? 最佳答案 也许您会考虑单元测试。我推荐你谷歌测试和谷歌模拟
我想在第二台显示器中移动一个窗口 (HWND)。问题是我尝试了很多方法,例如将分辨率加倍或输入负值,但它永远无法将窗口放在我的第二台显示器上。 关于如何在 C/C++/c# 中执行此操作的任何线索 最
我正在寻找 C/C++/C## 中不同类型 DES 的现有实现。我的运行平台是Windows XP/Vista/7。 我正在尝试编写一个 C# 程序,它将使用 DES 算法进行加密和解密。我需要一些实
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
有没有办法强制将另一个 窗口置于顶部? 不是应用程序的窗口,而是另一个已经在系统上运行的窗口。 (Windows, C/C++/C#) 最佳答案 SetWindowPos(that_window_ha
假设您可以在 C/C++ 或 Csharp 之间做出选择,并且您打算在 Windows 和 Linux 服务器上运行同一服务器的多个实例,那么构建套接字服务器应用程序的最明智选择是什么? 最佳答案 如
你们能告诉我它们之间的区别吗? 顺便问一下,有什么叫C++库或C库的吗? 最佳答案 C++ 标准库 和 C 标准库 是 C++ 和 C 标准定义的库,提供给 C++ 和 C 程序使用。那是那些词的共同
下面的测试代码,我将输出信息放在注释中。我使用的是 gcc 4.8.5 和 Centos 7.2。 #include #include class C { public:
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我的客户将使用名为 annoucement 的结构/类与客户通信。我想我会用 C++ 编写服务器。会有很多不同的类继承annoucement。我的问题是通过网络将这些类发送给客户端 我想也许我应该使用
我在 C# 中有以下函数: public Matrix ConcatDescriptors(IList> descriptors) { int cols = descriptors[0].Co
我有一个项目要编写一个函数来对某些数据执行某些操作。我可以用 C/C++ 编写代码,但我不想与雇主共享该函数的代码。相反,我只想让他有权在他自己的代码中调用该函数。是否可以?我想到了这两种方法 - 在
我使用的是编写糟糕的第 3 方 (C/C++) Api。我从托管代码(C++/CLI)中使用它。有时会出现“访问冲突错误”。这使整个应用程序崩溃。我知道我无法处理这些错误[如果指针访问非法内存位置等,
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 7 年前。
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,因为
我有一些 C 代码,将使用 P/Invoke 从 C# 调用。我正在尝试为这个 C 函数定义一个 C# 等效项。 SomeData* DoSomething(); struct SomeData {
这个问题已经有答案了: Why are these constructs using pre and post-increment undefined behavior? (14 个回答) 已关闭 6
我是一名优秀的程序员,十分优秀!