简体   繁体   中英

Enhancing C# syntactic sugar (or getting around “Cannot use ref or out parameter”)

Please ignore whether a pattern like this is actually a good idea or not. The rationale was that I wanted to catch an error that was thrown, or a mismatch in the number of records inserted / updated / deleted. I didn't want to repeat this logic, and at the time this customisation felt like it was going to apply to no more than about four long "script" methods.

My first step was to use an anonymous function.

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;
        }
    }
}

if I try to use this:

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

anonymousTypeWithClassInstanceInside <-- source of the error

The error comes up:

Cannot use ref or out parameter 'abc' inside an anonymous method, lambda expression, or query expression

The solution is to get rid of the delegate Func<bool> f . I'm writing this entry (perhaps it should be a blog post?) because it felt that the compile-time error gets generated is a bit of a road block.

In this post, I discovered a link to Eric's article:

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

here

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

After seeing how foreach gets implemented, I was lead to think... hmmm... maybe I'm reaching for customisable syntactic sugar.

Is this possible now in C#4? Will it be possible in C#5? It makes me consider looking into http://nemerle.org at some point in the future, but I'd really like to stay within C#.

In isolation, a way better way to write this code would be:

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);
    }
}

And on the outside:

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

or the equivalent. This eliminates the odd side-effectful function, the ref parameter, etc. And it's shorter.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM