可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Consider a function which returns two values. We can write:
// Using out:
string MyFunction(string input, out int count)
// Using Tuple class:
Tuple<string, int> MyFunction(string input)
// Using struct:
MyStruct MyFunction(string input)
Which one is best practice and why?
回答1:
They each have their pros and cons.
Out parameters are fast and cheap but require that you pass in a variable, and rely upon mutation. It is almost impossible to correctly use an out parameter with LINQ.
Tuples make collection pressure and are un-self-documenting. "Item1" is not very descriptive.
Custom structs can be slow to copy if they are large, but are self-documenting and are efficient if they are small. However it is also a pain to define a whole bunch of custom structs for trivial uses.
I would be inclined to the custom struct solution all other things being equal. Even better though is to make a function that only returns one value. Why are you returning two values in the first place?
UPDATE: Note that tuples in C# 7, which shipped six years after this article was written, are value types and hence less likely to create collection pressure.
回答2:
I think the answer depends on the semantics of what the function is doing, and the relationship between the two values.
For example, the TryParse
methods take a out
parameter to accept the parsed value, and return a bool
to indicate whether or not the parse succeeded. The two values don't really belong together, so, semantically, it makes more sense, and the intent of the code is easier to read, to use the out
parameter.
If, however, your function returns X/Y coordinates of some object on the screen, then the two values semantically belong together and it would be better to use a struct
.
I'd personally avoid using a tuple
for anything that will be visible to external code becuase of the awkward syntax for retrieving the members.
回答3:
Adding to the previous answers, C# 7 brings value type tuples, unlike System.Tuple
that is a reference type and also offer improved semantics.
You can still leave them unnamed and use the .Item*
syntax:
(string, string, int) getPerson()
{
return ("John", "Doe", 42);
}
var person = getPerson();
person.Item1; //John
person.Item2; //Doe
person.Item3; //42
But what is really powerful about this new feature is the ability to have named tuples. So we could rewrite the above like this:
(string FirstName, string LastName, int Age) getPerson()
{
return ("John", "Doe", 42);
}
var person = getPerson();
person.FirstName; //John
person.LastName; //Doe
person.Age; //42
Destructuring is also supported:
(string firstName, string lastName, int age) = getPerson()
回答4:
I will go with the approach of using Out parameter because in second approach you would require to create and object of Tuple class and then add value to it, which I think is an costly operation compared to returning the value in out parameter. Though if you want to return multiple values in Tuple Class (which infact can not be accomplished by just returning one out parameter) then I will go for second approach.
回答5:
You did not mention one more option, which is having a custom class instead of struct. If the data has semantics associated to it which can be operated upon by functions, or the instance size is big enough (> 16 bytes as a rule of thumb), a custom class may be preferred.
Use of "out" is not recommended in public API because of its association to pointers and requiring understanding of how reference types work.
https://msdn.microsoft.com/en-us/library/ms182131.aspx
Tuple is good for internal use, but it usage is awkward in public API.
So, my vote is between struct and class for public API.
回答6:
There is no "best practice". It is what you are comfortable with and what works best in your situation. As long as you are consistent with this, there is no problem with any of the solutions you've posted.