|
Sorry I didn't clarify in the question, but I didn't indicate that exception handling was implied. There are cases where success doesn't mean that the function ran without error and that using out could make the function more useful.
Example
public bool IsProductOnSale(guid productId, out Guid[] productResellerIds)
{
}
It's not the best example without a better understanding of the overall context. I'm just saying, if you have what you need already, why write multiple functions for what could be handled in one?
if (Object.DividedByZero == true) { Universe.Implode(); }
Meus ratio ex fortis machina. Simplicitatis de formae ac munus. -Foothill, 2016
|
|
|
|
|
Why not returning the array instead and interpreting an array-length of 0 as "product is not on sale"?
(The method should be renamed then of course.)
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
|
|
|
|
|
I thought it was a bad example. I'm just trying to start a discussion on the use of out and not focus too much on implementation specific code.
if (Object.DividedByZero == true) { Universe.Implode(); }
Meus ratio ex fortis machina. Simplicitatis de formae ac munus. -Foothill, 2016
|
|
|
|
|
clapclap wrote: Instead of an out Boolean to indicate success/failure, throwing exceptions provides more flexibility and scalability
This is a very bad practice. Exceptions are just that, when something exceptional happens. Exceptions are very expensive operations, they take a long time to create, have you seen all the information that is involved when an exception occurs? Using exceptions to control things you expect to happen, or can predict will happen, is very bad practice.
|
|
|
|
|
I find the argument that devs need skills to use out and ref a bit lame.
We're professional programmers and we write for other professional programmers. I really hope professional programmers grasp the concept of reference and value types or we're going to have bigger problems than out and ref.
That said, I still don't think the use of out and ref is particularly good design.
As a rule a function should do one thing only, that means it probably produces at most one value (in some cases that value is a complex object containing many properties).
Functions that are pure (i.e. have no side-effects) are easier to grasp, change, and maintain as well, using out or ref is a side-effect. Having pure functions is a more functional approach to programming (which we can't or should fully implement in C#, but still has benefits).
In most cases I prefer validating all input thoroughly, if success do the thing else return and show the user the validation results. If the thing still fails there must be something really wrong. Returning a bool won't do, an Exception is the way to go.
Of course the TryParse/TryWhatever and DivRem methods are acceptable exceptions to this rule. Mostly because it's convention, the alternative would be something like "if (int.CanParse(value)) int.Parse(value)".
In the case of DivRem the remainder is used to calculate the quotient and while you have the remainder anyway it would be a waste to throw it away just because returning two values is "bad design".
Personally I've never needed out or ref
|
|
|
|
|
I see your point when you run comparisons against the functional programming paradigm. In the question, I should have expanded to include that the function's success/failure result was outside of exception handling. The function should, by all means, throw exceptions but the usage of out really does seem best fitted to TryParse style functions where I want to know if the function did what it was supposed to do before I try to do anything with the result. With TryParse, it almost seems like it was designed to swallow certain exceptions and return false if they occur.
Let me see if I can give a better example. Let's go with an web environment that supports both internal and external user logon.
using System.DirectoryServices.AccountManagement;
public static class AuthorizationManager
{
public static bool UserHasAccess(string userName, out UserPricipal user)
{
}
}
The function basically answers two questions, is the user an internal user by populating the user parameter and that the user has the required access.
I am seeing similar approaches to this in the black box code of the Cryptography namespace. Like with RandomNumberGenerator.GetBytes(byte[]) where you pass a instantiated array of bytes to the function and it populates all the elements with random values. Why is this method used instead of byte[] rndAry = rand.GetBytes(n);? The deeper I delve into C# and .Net, the more contradictions I find.
if (Object.DividedByZero == true) { Universe.Implode(); }
Meus ratio ex fortis machina. Simplicitatis de formae ac munus. -Foothill, 2016
|
|
|
|
|
Foothill wrote: Let me see if I can give a better example. Let's go with an web environment that supports both internal and external user logon. I'm not sure I get what your function is doing. If the user has no access what happens to the principal (null, default/anonymous implementation)? Can the method return true and still set principal to null or return false and set the principal anyway? What if you decide for an anonymous principal (or whatever) instead, will it always return true? I'd have to read the documentation and trust it's correct (and I never trust documentation).
I'd expect (and prefer) something like the following, although I'm not sure how this would work out in actual code:
public static UserPrincipal GetPrincipal(string userName)
{
if (UserIsAuthorized(userName))
{
return new UserPrincipal();
}
else
{
return null;
}
} That code makes it pretty explicit that there is or isn't a UserPrincipal for a userName. The function now does one thing (retrieve the principal if it exists) and the function name makes it clear. There is also one less variable to worry about (which eliminates the questions I raised earlier).
Foothill wrote: The deeper I delve into C# and .Net, the more contradictions I find. Many people worked on it and all had their own ideas and experiences. That .NET does it like this doesn't mean it's good or makes sense. Or maybe it does, but we don't know why
I'd prefer the byte[] rndArr = rand.GetBytes(n); approach, but you might have guessed that
|
|
|
|
|
Ah, so even though I can find Microsoft code that does it, doesn't mean that it's encouraged or the best way to go about things. One takeaway that I am getting is that you should always write your code so simple that it lends to documenting itself even when you are writing companion documentation. I guess the out and ref keywords really are throwbacks to the old WIN32 API design style.
if (Object.DividedByZero == true) { Universe.Implode(); }
Meus ratio ex fortis machina. Simplicitatis de formae ac munus. -Foothill, 2016
|
|
|
|
|
Foothill wrote: Ah, so even though I can find Microsoft code that does it, doesn't mean that it's encouraged or the best way to go about things Even though Microsoft preaches best practice they don't always practice it themselves
Foothill wrote: One takeaway that I am getting is that you should always write your code so simple that it lends to documenting itself even when you are writing companion documentation That's an awesome takeaway!
|
|
|
|
|
Sander Rossel wrote: That's an awesome takeaway!
Just one of many, OG is giving me some great info on intricacies of exception handling below. There seems to be a leaning that out is only useful with TryParse style methods where you know exactly how a function could fail and you are given a choice how to handle it without having to surround the function with try...catch blocks.
if (Object.DividedByZero == true) { Universe.Implode(); }
Meus ratio ex fortis machina. Simplicitatis de formae ac munus. -Foothill, 2016
|
|
|
|
|
Foothill wrote: byte[] rndAry = rand.GetBytes(n);
Easy enough to implement with Extension Methods.
I might as well put some in my utility library. They likely will be useful.
What do you get when you cross a joke with a rhetorical question?
The metaphorical solid rear-end expulsions have impacted the metaphorical motorized bladed rotating air movement mechanism.
Do questions with multiple question marks annoy you???
|
|
|
|
|
Foothill wrote: With TryParse, it almost seems like it was designed to swallow certain exceptions and return false if they occur.
Yes it does seem that way, but that's not how TryParse works and it's important to understand that. As I mentioned above, exceptions are incredibly expensive and you should not be raising them lightly. The "Try" in TryParse doesn't mean there is an internal try\catch around the parse, it is there to indicate that it will try to do the work, but the work might fail. With Parse on its on there is no "might fail", with Parse we are saying that this operation is going to work because we know the text is of the correct format.
If you disassemble the code behind int.TryParse you will see that it verifies programmatically that the text can be converted to an int and if it detects otherwise it returns false - no exceptions or thrown or swallowed. int.TryParse is an incredibly frequently used function and if it generated exceptions internally then the resulting code would have incredibly poor performance.
|
|
|
|
|
Ah, that makes sense. TryParse is a very simple interface to what seems to be a complicated mechanism under the hood. Thanks for explaining it in greater detail.
if (Object.DividedByZero == true) { Universe.Implode(); }
Meus ratio ex fortis machina. Simplicitatis de formae ac munus. -Foothill, 2016
|
|
|
|
|
Sander Rossel wrote: I really hope professional programmers grasp the concept Been to QA recently?
|
|
|
|
|
Since when do programmers visit QA?
|
|
|
|
|
Well there are at least some who do! Guys like Richard who supply the responses. Most of them qualify
Never underestimate the power of human stupidity
RAH
|
|
|
|
|
I visit QA and try to find questions that I can answer but, for the most part, the senior programmers that haunt these halls give way better answers than I ever could.
if (Object.DividedByZero == true) { Universe.Implode(); }
Meus ratio ex fortis machina. Simplicitatis de formae ac munus. -Foothill, 2016
|
|
|
|
|
Alright, I guess the (sado-)masochistic programmers among us visit QA
|
|
|
|
|
The examples you've posted in reality would probably run asynchronous; in which case, any return values would be via call-backs and classes descended from EventArgs.
It's very hard justifying the use of "out" in an event-driven environment.
In any case, as soon as you need 2 "outs", it starts getting ugly / uglier.
|
|
|
|
|
I do use the out keyword, quite similar to your way of using it.
When looking for an item in a list with some condition, it is normally not an exceptional case when no item meets the condition (hence throwing an Exception is not appropriate). With a bool TryGetItemByCondition(object condition, out SomeType item) , there is no more need to check item for null , thus simplifying the code.
|
|
|
|
|
Thanks for the input. I see that it might be good practice that when I also use out , I should clearly label the functions TrySomething(params, out result) to bring them more in line with established syntax.
if (Object.DividedByZero == true) { Universe.Implode(); }
Meus ratio ex fortis machina. Simplicitatis de formae ac munus. -Foothill, 2016
|
|
|
|
|
I occasionally use the out or ref keyword, but not often at all. Personally, if I need to return more than just some result I create a class. eg. In my DAL component I have a method that executes a given query and returns a Result class instance (Result is a class I wrote, nothing .Net native)
The result class in my case contains:
* The Dataset with the returned results (if any)
* A status enum
* An exception (can be null if everything goes well)
* The executed SQL
That way I have one line of code that executes the statement and one of code that will check wether that went OK or not. Notice that I have for more flexibility in my return object and if I ever need to change it (eg add a property to the Result class) I have a lesser change of high impact on the rest of the code.
in short. out and ref can be useful in some cases, but I would not use it as a rule of thumb.
That's my opinion anyway.
|
|
|
|
|
I've also created a similar class class ,FunctionResult<t>, that used generics to populate the result with an accompanying status enum and an optional exception field. I understand the point that Sander eluded to previously that a Result class add an extra layer of complexity that might cause confusion to other developers if they are not familiar with it or if it is not a standard code practice in your organization.
if (Object.DividedByZero == true) { Universe.Implode(); }
Meus ratio ex fortis machina. Simplicitatis de formae ac munus. -Foothill, 2016
|
|
|
|
|
Sometimes, you need out because any other solution would be unwieldy. For example, look at TryParse and TryParseExact:
public static bool TryParse(string s, out int result) The difference between Convert.ToInt32 and Int32.TryParse is that the former throws an exception, the later returns an OK / Fail result - and needs the out parameter in order to return the value as well.
While you could use Convert instead, and wrap it with a try...catch block to get the same effect, using exceptions as part of normal processing is an anathema to me (and is rather ugly). And user input checking is (or should damn well be) part of normal processing.
You could create a Generic type which contains the value and a bool but that's ugly and annoying as well.
One out or ref for good reason is OK - if it starts to get more than that, then probably it needs a class or struct instead.
And I do find the "using out and ref requires an understanding of pointers" a bit specious as well: if you don't understand them, then I suspect that an out parameter is the least of your problems!
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
|
|
|
|
|
Thank you for the input. I think I am getting a better understanding of when to use
out<\out>. Would you say that a fair example is in functions that accept user inputs and can throw or rethrow several different exceptions of which only a couple represent an actual problem and the rest of them represent errors in the user's input that can be safely swallowed within the function?<br />
<div class="signature">if (Object.DividedByZero == true) { Universe.Implode(); }<br />
Meus ratio ex fortis machina. Simplicitatis de formae ac munus. -Foothill, 2016</div>
|
|
|
|