问题
I'm reading up on core C# programming constructs and having a hard time wrapping my head around the out
parameter modifier. I know what it does by reading but am trying to think of a scenerio when I would use it.
Can someone give me a real-world example? Thanks.
回答1:
there are many scenarios where you would use it, but the main one would be where your method needs to return more then one parameter. Take, for example, the TryParse
methods on int
type. In this case, instead of throwing an exception a bool is returned as a success/failure flag and the parsed int is return as the out param. if you were to call int.Parse(...)
you could potentially throw an exception.
string str = "123456";
int val;
if ( !int.TryParse(str,out val) )
{
// do some error handling, notify user, etc.
}
回答2:
The main motivation to using an out
parameter is to allow a function to return multiple values to the caller and everyone else provided examples in the framework. I'll take a different approach to answering your question by exploring the reasoning behind having out
parameters in the first place. I won't write out actual examples but describe them.
Normally you have only one mechanism to return values, the function's return value. Sure you could use a global (static) or instance variables too but that's not very practical nor safe to do in general (for reasons I won't explain here). Prior to .NET 3.5, there wasn't a really practical way to return multiple values from a function. If out
or ref
modifiers were not available, you would have a few options:
If all your values had the same type, you could return some collection of the values. This is perfectly fine in most cases, you could return an array of number, list of strings, whatever. This is perfect if all the values were related in exactly the same way. i.e., All numbers were the number of items in a container, or the list was of names of guests at a party. But what if the values you returned represented different quantities? What if they had different types? A list of objects could hold them all but it is not a very intuitive way to manipulate that sort of data.
For the case when you need to return multiple values of different types, the only practical option you had was to create a new class/struct type to encapsulate all these values and return an instance of that type. Doing so you could return strongly typed values with intuitive names and you could return multiple values this way. The problem is that in order to get that, you had to define the type with a specific name and everything just to be able to return multiple values. What if you wanted to return only two values which were simple enough making it impractical to create a type for it? You have a couple more options again:
You could create a set of generic types to contain a fixed amount of values of varying types (like a tuple in functional languages). But it is not as appealing to do so in a reusable manner since it wasn't part of the framework at the time. It could be put in a library but now you add a dependency on that library just for the sake of these simple types. (just be glad that .NET 4.0 now includes the
Tuple
type) But this still doesn't solve the fact that these are simple values which means added complexity for a simple task.The option that was used was to include an
out
modifier which allows the caller to pass a "reference" to a variable so that the function may set the referenced variable as another way to return a value. This way of returning values was also available in C and C++ in many ways for the same reasons and played a role in influencing this decision. However the difference in C# is that for anout
parameter, the function must set the value to something. If it doesn't, it results in a compiler error. This makes this less error prone since by having anout
parameter, you're promising the caller that you will set the value to something and they can use it, the compiler makes sure you stick to that promise.
A note on the typical usage of the out
(or ref
) modifier, it will be rare to see more than one or two out
parameters. In those cases, it will almost always be a much better idea to create the encapsulating type. You would typically use it if you needed just one more value to return.
However since C#-3.0/.NET-3.5 with the introduction of anonymous types and tuples introduced in .NET 4.0, these options provided alternative methods to return multiple values of varying types easier (and more intuitive) to do.
回答3:
Sure, take a look at any of the TryParse
methods, such as int.TryParse:
The idea is you actually want two pieces of information: whether a parse operation was successful (the return value), and, if so, what the result of it actually was (the out
parameter).
Usage:
string input = Console.ReadLine();
int value;
// First we check the return value, which is a bool
// indicating success or failure.
if (int.TryParse(input, out value))
{
// On success, we also use the value that was parsed.
Console.WriteLine(
"You entered the number {0}, which is {1}.",
value,
value % 2 == 0 ? "even" : "odd"
);
}
else
{
// Generally, on failure, the value of an out parameter
// will simply be the default value for the parameter's
// type (e.g., default(int) == 0). In this scenario you
// aren't expected to use it.
Console.WriteLine(
"You entered '{0}', which is not a valid integer.",
input
);
}
Many developers complain of out
parameters as a "code smell"; but they can be by far the most appropriate choice in many scenarios. One very important modern example would be multithreaded code; often an out
parameter is necessary to permit "atomic" operations where a return value would not suffice.
Consider for example Monitor.TryEnter(object, ref bool)
, which acquires a lock and sets a bool
atomically, something that wouldn't be possible via a return value alone since the lock acquisition would necessarily happen before the return value were assigned to a bool
variable. (Yes, technically ref
and out
are not the same; but they're very close).
Another good example would be some of the methods available to the collection classes in the System.Collections.Concurrent
namespace new to .NET 4.0; these provide similarly thread-safe operations such as ConcurrentQueue<T>.TryDequeue(out T)
and ConcurrentDictionary<TKey, TValue>.TryRemove(TKey, out TValue)
.
回答4:
Output parameters are found all over the .NET framework. Some of the uses I see most often are the TryParse methods, which return a boolean (indicating whether or not the parse was valid) and the actual result is returned via the output parameter. While it's also very common place to use a class when you need to return multiple values, in such an example as this it's a little heavy handed. For more on output parameters, see Jon Skeet's article on Parameter passing in C#.
回答5:
Simple, when you have a method that returns more than one value. One of the most "famous" cases is Dictionary.TryGetValue:
string value = "";
if (openWith.TryGetValue("tif", out value))
{
Console.WriteLine("For key = \"tif\", value = {0}.", value);
}
else
{
Console.WriteLine("Key = \"tif\" is not found.");
}
回答6:
As others have said - out parameters allow us to return more than one value from a method call without having to wrap the results in struct/class.
The addition of the xxx.TryParse methods greatly simplified the coding necessary to convert between a string value (frequently from the UI) and a primitive type.
An example of what you might have had to write to achieve the same functionality is here:
/// <summary>
/// Example code for how <see cref="int.TryParse(string,out int)"/> might be implemented.
/// </summary>
/// <param name="integerString">A string to convert to an integer.</param>
/// <param name="result">The result of the parse if the operation was successful.</param>
/// <returns>true if the <paramref name="integerString"/> parameter was successfully
/// parsed into the <paramref name="result"/> integer; false otherwise.</returns>
public bool TryParse(string integerString, out int result)
{
try
{
result = int.Parse(integerString);
return true;
}
catch (OverflowException)
{
// Handle a number that was correctly formatted but
// too large to fit into an Int32.
}
catch (FormatException)
{
// Handle a number that was incorrectly formatted
// and so could not be converted to an Int32.
}
result = 0; // Default.
return false;
}
The two exception checks that are avoided here make the calling code much more readable. I believe that the actual .NET implementations avoid the exceptions altogether so perform better as well. Similarly, this example shows how IDictionary.TryGetValue(...) makes code simpler and more efficient:
private readonly IDictionary<string,int> mDictionary = new Dictionary<string, int>();
public void IncrementCounter(string counterKey)
{
if(mDictionary.ContainsKey(counterKey))
{
int existingCount = mDictionary[counterKey];
mDictionary[counterKey] = existingCount + 1;
}
else
{
mDictionary.Add(counterKey, 1);
}
}
public void TryIncrementCounter(string counterKey)
{
int existingCount;
if (mDictionary.TryGetValue(counterKey, out existingCount))
{
mDictionary[counterKey] = existingCount + 1;
}
else
{
mDictionary.Add(counterKey, 1);
}
}
And all thanks to the out parameter.
回答7:
bool Int32.TryParse(String, out Int);
or something similar like Dictionary.TryGetValue.
But I would consider this one to be a not too good practice to employ it, of course, using those provided by API like the Int32 one to avoid Try-Catch is exceptions.
回答8:
The other answers have shown how out
parameters allow you to return more than one value from a method.
I would like to describe another distinct advantage of out
parameters: Improving the public interface of a class by making usage mistakes less likely.
First attempt (flawed)
Let's consider the following interface:
interface IUndoableCommand
{
void Execute();
IUndoableCommand GetUndoCommand();
}
This is the Command pattern for commands that can be undone. However, something is wrong with this interface design: It allows you to write code that undoes a command before it has even been executed:
someCommand.GetUndoCommand().Execute();
someCommand.Execute();
// ^ this is obviously wrong, but will compile.
Second attempt (still flawed)
So let's try to prevent this mistake by re-designing the interface:
interface IUndoableCommand
{
IUndoableCommand Execute();
}
Now you only have access to the "undo" command once the undoable command has been executed.
var undoCommand = someCommand.Execute();
undoCommand.Execute();
// ^ this is better, but has other problems...
While this design is not explicitly bad, it suffers from two flaws:
It's not very logical for an
Execute
method to return something. If it returns something at all, you'd probably think it's some sort of success indicator ("result
"), but not another "undo" command.It allows you to "chain" calls to
Execute
in the form of:someCommand.Execute().Execute(); // ^^^^^^^^^^^^^^^^^^^^ // guess what this does!?
Contrary to what it looks like,
someCommand
will not be executed twice; it will effectively be cancelled.
Final attempt with out
parameter (successful)
So once again, let's try to improve the interface by replacing the return value with an out
parameter:
interface IUndoableCommand
{
void Execute(out IUndoableCommand undoCommand);
}
This design suffers from none of the disadvantages shown above:
- You cannot call the "undo" command before the actual command.
Execute
has no return value (or it could have a success indicator return value), as it should be.You cannot chain the
Execute
method of the "undo" command to theExecute
method of the actual command.IUndoableCommand undoCommand; someCommand.Execute(out undoCommand); undoCommand.Execute(… /* out someCommand */);
(Of course once you have references to both commands, you could still do mischief, such as calling the undoCommand
twice, but there's probably no way from stopping that at compile-time.)
回答9:
//out key word is used in function instead of return. we can use multiple parameters by using out key word
public void outKeyword(out string Firstname, out string SecondName)
{
Firstname = "Muhammad";
SecondName = "Ismail";
}
//on button click Event
protected void btnOutKeyword_Click(object sender, EventArgs e)
{
string first, second;
outKeyword(out first, out second);
lblOutKeyword.Text = first + " " + second;
}
来源:https://stackoverflow.com/questions/4102892/real-world-examples-where-c-sharp-out-parameters-are-useful