-8

Hi guys i have a question which is : how to directly execute a function or delegate as well as returning a value from a method using one single Expressional return statement in C#

*For reference, I am using C#7

In this context I need to return to the user an Object which has been generated like this:

object obj = cmd.ExecuteScalar();

I also have a logging function which I want to call:

DoLog(cmd.CommandText)

PS: I am 100% aware that I can achieve this non-conditionally using a couple of if statements in a much easier way - i'm simply asking if anyone can provide me with the most elegant "one liner" approach possible.

Here is a standard example of how conditional return expressions usually work

return (someBool)
   ? value1
   : value2;

this will return 'value1' if someBool is TRUE;

Basically my question is can i do something like this

return (chkStatus)
    ? (obj & DoLog(cmd.CommandText))
    : null;

In response to some of you who have suggested that I can use a single pipe operator to split the return, while your suggestions are in good faith, I assume you never tried to compile them

If you had you would see that it doesn't work as the types: Object and Void are NOT directly comparable thus causing a compile time error.

My request for a more refined / elegant method still stands.

Community
  • 1
  • 1
jasttim
  • 723
  • 8
  • 19
  • 1
    Why not simply `if (chkStatus) { DoLog(cmd.CommandText); return obj; } return null;`? – Corak Sep 12 '19 at 12:10
  • so DoLog is my own logging class. and I parse into that the value of CMD which exists at the time ExecScalar is called. It does NOT need to be called if chkStatus==false. hence I am wondering if I can get C# to do the log and return at the same time from a Conditional if statement. Sure.. I can probably use a standard IF.. But this question was really just out there to see if I could push the boat out, catch my drift ? – jasttim Sep 12 '19 at 12:12
  • 5
    Uhh..... `catch { throw; }` ??? useless? – Jeroen van Langen Sep 12 '19 at 12:13
  • If you want one-liner (though I am not sure what you actually asking, that C# 7.0 part is especially mysterious), then maybe you can utilize [fluent interface](https://stackoverflow.com/q/1622662/1997232). – Sinatr Sep 12 '19 at 12:14
  • Try : return (obj == null) ? false : DoLog(cmd.CommandText); – jdweng Sep 12 '19 at 12:15
  • 4
    @jasttim - yeah, but why even catch if all you do is throw again? -- you can do try..finally without catch. – Corak Sep 12 '19 at 12:16
  • @jasttim You mean, it ruins the stacktrace.. If any exception is thrown, your stacktrace will point to the `throw;` and not the actual line where the exception occured. – Jeroen van Langen Sep 12 '19 at 12:19
  • 2
    If you don't catch the exception, it won't be "eaten"...Only catch exceptions which you can solve. – Jeroen van Langen Sep 12 '19 at 12:20
  • 3
    You can completely omit your `catch { throw; }`. It will do exactly the same as having only `try finally` without any `catch` at all. Anyway I still don´t get your actual question. – MakePeaceGreatAgain Sep 12 '19 at 12:21
  • Do you want to know how to execute multiple statements within a single return and somewhat "combine their return-values"? This `(obj & DoLog(cmd.CommandText))` makes me assume that. – MakePeaceGreatAgain Sep 12 '19 at 12:24
  • You obiously cant do: ``` return (chkStatus) ? (obj & DoLog(cmd.CommandText)) : null; ``` but what you can do is something very simmilar: ``` Enable(async); //whatever if (obj != null) { DoLog(cmd.CommandText); Disable(); return obj; } Disable(); return null; ``` – Prophet Lamb Sep 12 '19 at 12:26
  • In relation to the single pipe alternation yes i know about that but it will not work due to Error CS0019 Operator '&' cannot be applied to operands of type 'object' and 'void' – jasttim Sep 12 '19 at 12:32
  • 1
    This question probably belongs on [codereview](https://codereview.stackexchange.com). *Personally* I would rewrite your code to this, without losing any functionality or making it unclear: https://gist.github.com/lassevk/ba8d967954b642111ea9eb4f1b277cf7 – Lasse V. Karlsen Sep 12 '19 at 12:35
  • 3
    In terms of your original problem, I think I'd go with `if (obj != null) { DoLog(cmd.CommandText); } return obj;` Simple, clear and concise. – mjwills Sep 12 '19 at 12:53
  • 2
    @jasttim - please explain in detail, how *exactly* your log will look *any* different with `catch { throw; }` and without. Because there won't be any differences. -- Now `catch (Exception e) { throw e; }` on the other hand... – Corak Sep 12 '19 at 12:54
  • @Corak [catch e -> throw e] does the same thing.. the exact same exception gets passed up, so as long as you have something higher up to intercept it you will still get full exception details and stack trace, you are welcome – jasttim Sep 13 '19 at 10:08
  • It's not about "higher up" it's about "further down". https://learn.microsoft.com/dotnet/csharp/language-reference/keywords/throw - "You can also use the throw e syntax [...] to instantiate a new exception [...]. In this case, **the stack trace of the original exception, [...] is not preserved.**" (emph. mine) -- the point everyone is making is: `catch { throw; }` is absolutely useless! Your "higher up" error handler will get the exact same information and stack trace as if you completely omit that. – Corak Sep 13 '19 at 10:21
  • **IF** you don't care about stack trace information from "lower" methods as you alluded to, you can remove that "lower" information by using `catch (Exception e) { throw e; }`. But you don't. – Corak Sep 13 '19 at 10:22
  • @Corak I care about ALL the error information hence why I don't do it that way.. I just don't care about it at THAT specific point in time - either way.. i have reworded the question so that my functional error handling doesn't attract any more comments exactly like yours which aren't helpful given my specific application context – jasttim Sep 13 '19 at 10:32
  • 1
    I agree, that catch-throw-stuff has put this question in an entirely cluttered discussion, which is completely off-topic for the topic. Thanks for deleting that stuff from the question. – MakePeaceGreatAgain Sep 13 '19 at 11:05
  • I ask that you all re-read my original request, with all the noise (my fault) removed. as im looking to expand on the solution in a method that may even surpass xdecdec & Gvs's original answers – jasttim Sep 13 '19 at 13:24
  • I re-read the request without the noise and I'm still baffled: why would anyone ever want to have such a reversed logic of "return, but also do this" instead of the simplicity of "do this and then return"? With the condition you have two return statements, yes, but you *are* allowed to do that. It's not like a `goto` that can potentially make the code unreadable. It's easy to understand, tried and tested. Also, there is no mysteriously secret `&?!` operator, that people try to hide from you, that would do exacly what you want. So what kind of answer would you expect, really? – Corak Sep 13 '19 at 14:40
  • @jasttim - **you** just resurrected this old thing just to "win" an argument noone is talking about anymore. And kept missing the point again: why even catch if all you do is throw? We all agree that `throw ex;` removes the stack trace. And we all agree, that there *is* merrit in `catch (SomeException ex) { /* do something */ throw; } ` so that the exception with all the stack information can bubble up. What everyone *except* you agrees is that `catch { throw; }` is useless, as it is a no-op that does nothing except taking up space in the source file and slow down the compiler for a nanosec. – Corak Sep 17 '19 at 11:05
  • @jasttim - you gave a valid reason, why one should prefer `throw;` over `throw ex;`. You keep failing to give an answer to the simple question: why even catch? – Corak Sep 17 '19 at 11:12
  • @Corak - Dont bother doing throw EX unless the processing on Ex happens inside of that particular catch block. else, just use throw; which PRESERVES the stack trace according to the documentation – jasttim Sep 17 '19 at 11:13
  • @jasttim - yes, we agree that `throw;` preserves the stack. The question is, why do you even `catch`? – Corak Sep 17 '19 at 11:15
  • @Corak so that when you are debugging, you are taken to the throw; for the failed block? - This is just an easier way of finding where problems come from and there is a negligible performance decrease in doing it this way, and even if there was, why would it matter, as youd be in error state anyway? – jasttim Sep 17 '19 at 11:17
  • @jasttim - ah, now we're getting somewhere. You have it for debugging. That raises more questions, but we're off topic as it is. – Corak Sep 17 '19 at 11:34

2 Answers2

1

I assume you want to return obj, but also execute DoLog which i'll assume has no return value. If you're dead set on wanting to extract this logic, you could write yourself an extension method:

public static T AndExecute<T>(this T returnValue, Action action)
{
    action();
    return returnValue;
}
return (chkStatus) ? obj.AndExecute(() => DoLog(cmd.CommandText)) : null;

Although for sake of readability i would leave it as is - it clearly conveys its purpose.

skolldev
  • 1,179
  • 9
  • 19
  • This was my first thought, by the network parent class containing this class HAS to be an instance member, and so a lot of the operations i need to perform are tailored for a specific network instance – jasttim Sep 12 '19 at 12:39
  • 1
    "by the network parent class containing this class HAS to be an instance member" Eeeehm, what? Could you please show how those two classes relate? I don´t get your structure. – MakePeaceGreatAgain Sep 12 '19 at 12:58
  • i resolved this by making a second static class ClassName inside the Network.cs file. this allows the use of the this modifier for importing parameters, as you cannot use for instance "this string str, int i" as the parameters for a method within an instance class, the compiler will not let u – jasttim Sep 12 '19 at 14:00
  • @HimBromBeere basically i have an instance class "Network", your method wont apply to that class because you cannot use a dynamic / static override method within an instance class - so in the same namespace is an extender class using your code which I can then call from my main Network library :) hope that clears it up – jasttim Sep 12 '19 at 15:23
1

You can work something out using lambda's, but it will not be a clean syntax:

        return (chkStatus 
            ? (Func<object>)(() => { 
                  DoLog(cmd.CommandText); 
                  return obj; 
              })
            : () => null)();
GvS
  • 52,015
  • 16
  • 101
  • 139
  • This could be written better with local method: `return chkStatus ? GetResult() : null;` `object GetResult() { DoLog(cmd.CommandText); return obj; }` – Xiaoy312 Sep 12 '19 at 21:51