【问题标题】:Enhancing C# syntactic sugar (or getting around "Cannot use ref or out parameter")增强 C# 语法糖(或绕过“不能使用 ref 或 out 参数”)
【发布时间】:2012-03-05 04:56:50
【问题描述】:

请忽略这样的模式是否真的是个好主意。基本原理是我想捕获抛出的错误,或者插入/更新/删除的记录数不匹配。我不想重复这个逻辑,当时这种定制感觉就像它只适用于不超过四个长的“脚本”方法。

我的第一步是使用匿名函数。

public void DoSqlAction(Func<bool> f, string task, string ctx, ref bool cont, List<ResultInfo> resultInfo) {
    if (cont) {
        bool ret = false;
        try {
            if (f.Invoke()) {
                resultInfo.Add(new ResultInfo(seq, task, "Success", ctx, true));
                cont = true;
            } else {
                resultInfo.Add(new ResultInfo(seq, task, "Fail", ctx, false));
                cont = false;
            }
        } catch (Exception ex) {
            resultInfo.Add(new ResultInfo(seq, task, "Error: " + ex.Message, ctx, false));
            cont = false;
        }
    }
}

如果我尝试使用它:

 DoSqlAction(() => 1 == cx.Execute(someSql, anonymousTypeWithClassInstanceInside), "add item", refinfo.ToString(),ref cont, resultInfo);

anonymousTypeWithClassInstanceInside

出现错误:

不能在匿名方法、lambda 表达式或查询表达式中使用 ref 或 out 参数“abc”

解决方案是摆脱委托Func&lt;bool&gt; f。我正在写这篇文章(也许它应该是一篇博客文章?),因为它觉得生成的编译时错误有点障碍。

在这篇文章中,我发现了 Eric 文章的链接:

C# Cannot use ref or out parameter inside an anonymous method body

这里

http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx

在看到 foreach 是如何实现的之后,我开始思考......嗯......也许我正在寻找可定制的语法糖。

现在在 C#4 中可以实现吗?在 C#5 中有可能吗?这让我考虑在未来的某个时候研究http://nemerle.org,但我真的很想留在 C# 中。

【问题讨论】:

  • 考虑 +1 仅仅是因为“......感觉生成的编译时错误 [that] 有点障碍。”我不明白为什么你不能在这里只返回 true 或 false 并确定将 SqlActions 列表作为输入的调用方法中的延续 - 是否有其他代码实际使用 cont?
  • 为什么你不直接写它来返回 true 或 false 来表示成功或失败,而不是使用 ref 参数?或者返回一个 ResultInfo。我错过了什么吗?
  • @arootbeer:同意。这就是我所说的“撕掉 Func f”的意思。这只是在这种情况下代码演变的方式+在实现重用模式之后,我必须去更改所有内容。 “布尔”的想法不如 lamda 灵活......如果我们最终发现有一个我们想要实现的横切模式怎么办?发现这一点后,我倾向于总是使用某种 OO 模式,而不是基于语言的想法。
  • ... 和 lamdas 是强大的小设备。我不喜欢这样的东西绊倒。也许它与 lamda 领域有关。
  • 我不会评论你写的代码是否好。但它应该编译得很好。而且由于您在 lambda 表达式中没有任何 refout,我认为您的问题不在您发布的代码中。

标签: c# .net lambda


【解决方案1】:

单独来看,编写此代码的更好方法是:

public ResultInfo DoSqlAction(Func<bool> f, string task, string ctx) {
    try {
        if (f.Invoke()) {
            return new ResultInfo(seq, task, "Success", ctx, true);
        } else {
            return new ResultInfo(seq, task, "Fail", ctx, false);
        }
    } catch (Exception ex) {
        return new ResultInfo(seq, task, "Error: " + ex.Message, ctx, false);
    }
}

在外面:

while (/* there's stuff to do */) {
    var result = DoSqlAction(/* the next stuff */);
    infos.Add(result);
    if (!result.Succeeded)
        break;
}

或等价物。这消除了奇怪的副作用函数、ref 参数等。而且它更短。

【讨论】:

  • 原来“ref bool cont, List resultInfo”是closure的一部分。该函数在本地包装。所以这种方法更加优雅,并且对于长脚本来说是有意义的。当您尝试将 ref 类型(即类实例)传递给 Func f 时,麻烦就来了。所以这个答案是行不通的。
  • 我仍然不明白为什么作为闭包的一部分会使这个解决方案出错;也许如果你提供更多的上下文,它会变得清晰?
  • 更正关闭评论。我的结束评论应该与优雅有关(我应该留在话题上)。 c 出现问题 -- DoSqlAction(method("string", new { a = 1, b = 2, c = new ClassAbc(1,2,3,4) }))
猜你喜欢
  • 2011-05-13
  • 1970-01-01
  • 1970-01-01
  • 2010-11-24
  • 1970-01-01
  • 1970-01-01
  • 2010-11-03
  • 2011-05-29
相关资源
最近更新 更多