After a long journey I realized I can make my code 20 times more readable thanks out parameter which I convieniently never knew about. However I several questions regarding feasibility and performance, take following pseudo code as example:
public static class Mention
{
class Item
{
public string gameObjectName;
public GameObject gameObject;
public MyComponent myComponent;
}
static Dictionary<int, Item> test = new Dictionary<int, Item>();
public static void OldMethod(int targetId, GameObject original)
{
GameObject newgo = GameObject.Instantiate(original) ?? throw new Exception("Spawn failed");
MyComponent mc = newgo.GetComponent<MyComponent>() ?? throw new Exception("Not found component");
try
{
test[targetId] = new Item
{
gameObject = newgo,
myComponent = mc,
gameObjectName = newgo.name
};
}
catch (Exception e)
{
throw new Exception("Failed");
}
}
public static void NewMethod(int targetId, GameObject original)
{
GameObject newgo = GameObject.Instantiate(original);
if (newgo.TryGetComponent(out MyComponent mc))
{
if (test.TryGetValue(targetId, out Item targetItem))
{
targetItem = new Item
{
gameObject = newgo,
myComponent = mc,
gameObjectName = newgo.name
};
}
else
{
throw new Exception("Failed");
}
}
else
{
throw new Exception("Not found component");
}
}
}
Either way I will be forced to use out parameters out of pure necessity, just like System.Linq, where utility and readability are outweighing the performance cost. But I was wondering whether it was performant of me to use out parameters in places where they might not entirely be necessary. So Iâm left with couple questions which arenât exclusive to out. My issue isnât readability in general public. I intent on âabusingâ out in reusable code
Is there a performance impact for any return value that isnât assigned? If I return a massive struct/class/ValueTuple, but that method doesnât assign it to any variable, is there something that I should be doing differently? Does that cost?
Is there any performance gain on operating on the âoutâ output exclusively without assigning it to anything as opposed to getting the output regular way into a variable and executing operations on that variable (assuming its a class)?
What would you say is the more appropriate way to obtain Item in these scenarios:
public static void x()
{
Item item = itemList[777] ?? throw new Exception();
// vs.
if (!itemList.TryGetValue(777, out Item item)) throw new Exception();
MyComponent mc = gameObject.GetComponent<MyComponent>() ?? throw new Exception();
// vs.
if (!gameObject.TryGetComponent(out MyComponent mc)) throw new Exception();
bool retItem = itemList.TryGetValue(777, out Item item);
// vs.
(bool, Item) retItem = itemList[777];
}
The cost is in the construction and allocation of memory for the return value. For the actual âreturnâ part, the cost is just the cost of copying a single reference (for reference types) or copying the entire struct (for value types). That cost is not large. Whether or not you assign it to any variable in the calling code doesnât make any difference as far as I know.
If youâre talking about assigning to a local variable vs declaring a variable inline with the out parameter, there is no difference. E.g. the following two are exactly the same performance-wise and most likely compile to the same result:
gameObject.TryGetComponent<MyComponent>(out MyComponent mc);
// vs
MyComponent mc;
gameObject.TryGetComponent<MyComponent>(out mc);
Individual cases:
Item item = itemList[777] ?? throw new Exception();
// vs.
if (!itemList.TryGetValue(777, out Item item)) throw new Exception();
If youâre going to throw an exception if the itemList doesnât exist, the top one probably makes more sense I guess. Although these bits of code actually do different things. The top will already throw an exception if the index is out of range, and it will only be null if thereâs an actual explicit null entry in the list. The bottom one tells you if there was a thing at that index or not. From a performance perspective they are pretty much identical. The point of TryGetValue type functions is generally to avoid the use of exceptions for program control though.
MyComponent mc = gameObject.GetComponent<MyComponent>() ?? throw new Exception();
// vs.
if (!gameObject.TryGetComponent(out MyComponent mc)) throw new Exception();
Again, same as the previous.
bool retItem = itemList.TryGetValue(777, out Item item);
// vs.
(bool, Item) retItem = itemList[777];
These two bits of code do different things. The top one looks like a List. The bottom one assumes a list of tuples: List<(bool, Item)>.
Additionally, the TryGetValue version is generally for when youâre not certain that the element will exist. The other version is for when you are certain it will exist. Performance-wise they will be extremely similar. Not different in any noticeable way.
Iâm going to start by saying⌠the performance difference between out and not out is pretty much none. Sure they technically allocate differently (an out actually requires a large stack frame than a return, but it makes up for it by utilizing a memory pointer for copying), they end up being reallllllllly similar that you wonât see any real world difference in the âoutâ itself. You may see performance difference based on the code youâve written around it to meet the requirements of your chosen pattern.
Well some performance considerations to make off the bat is if you write custom âoutâ versions of methods that just call another method and make a check on it will be slower if only because youâre turning a single call into 2 calls.
But using existing âTry->outâ methods, or writing those that donât just wrap existing methods, there arenât many performance differences that youâd have to really consider. Actually sometimes it could actually be faster.
Well⌠for starters âoutâ parameters require that the parameter is set. You canât not set it.
Take for instance this method:
public static void Foo(out int value)
{
Debug.Log("Foo");
}
This will throw a compiler error since âvalueâ was never set. ârefâ on the other hand allows you to not have to set the parameter. Basically the difference between out and ref is that:
ref - allows passing in a variable reference that can be read/write
out - allows passing in a variable reference that must be set (can not be read until itâs set)
So not setting it is kind of moot.
Now if you mean not setting it before calling the âTry->outâ method⌠oh that doesnât matter. You donât have to set the variable, the out does that for you. Itâs actually less performant to set it. For example⌠lets say you had this:
Vector3 v;
SomeMethod(out v);
vs
int v = new Vector3(1,1,1);
SomeMethod(out v);
Technically the second is slower because youâre doing extra work. Youâve called a Vector3 constructor with values. And since the âoutâ overwrites âvâ, there was no point in even doing it. The first option is the preferred option here.
Iâm not sure what you mean⌠are you saying the difference between these?
var c = gameObject.GetComponent<MyComponent>();
if(c != null)
{
c.Foo();
}
In this situation⌠that really depends on how TryGetComponent and GetComponent are implemented.
The actual variable allocations are the same on the stack. And the actual implications of stack allocations vs the 2 methods are negligeable vs each other (out has a larger stack frame, but uses a memory pointer which counters the smaller stack frame of the none out).
But the underlying code will have a huge outcome on things. How does TryGetComponent determine returning true or false?
But thatâs incidental. And can vary from method to method.
More appropriate? Thatâs subjective.
For the first one⌠well the 2 behave vastly differently. âitemList[777]â has the potential to throw a IndexOutOfRangeException, where as the âitemList.TryGetValueâ will not. So technically theyâre not even the same thing. (consider if you wrapped the method âxâ in a try/catch and only caught for IndexOutOfRangeException, youâd get very different behaviour from both implementations)
Iâm going to come back to the 2nd since thereâs a LOT to discuss on that one.
In the case of the 3rd, weâre back to the first one in that they donât have the same behaviour since [777] can throw an indexoutofrangeexception. Furthermore in this example you seem to imply that itemList is a tuple in the 2nd which further alters the behaviour. The list is no longer the same kind of list.
âŚ
Now back to that middle one.
Now⌠at first glance these would appear to behave similarly. If we pretended that GetComponent didnât return an object that was a UnityEngine.Object, but rather a normal C# object, then these 2 would be pretty much the same thing. The only difference would be the generated IL underneath, and the â?? throwâ technically has more efficient IL since it doesnât stloc the âmcâ variable until after running a brtrue.s instruction on the top of the stack for the returned object. Where as the TryGetComponent technically would be more due to how it has to assign obj before the branch.
But I mean⌠this performance gain is only applies when an exception was thrown. So not only is it teeny weeny⌠it also implies your application has entered an unwanted state. So thereâs that to consider.
BUT, there is another implication to consider.
Unity overrides the == and != operators for all UnityENgine.Objects (this includes Components). Unity has a special override that evaluates as null even if itâs not null, but it has been destroyed.
Problem is the ?? does not respect this override of the == and != operators.
So if these were normal C# objects, the 2 would be the same.
But since theyâre not, and actually have this weird behaviour to them⌠these 2 arenât actually the same behaviour wise.
âŚ
So which is more appropriate?
Well, since none of the 3 examples behave the same as each other. The appropriate choice depends on what behaviour you actually want/need.
âŚ
TLDR;
Performance isnât my concern here.
What is though is understanding the implications of the patterns you choose to use since all your examples behave differently depending on your pattern chosen.