问题
I have just refactored a colleague's code that, roughly, looked like this...
public class Utility
public void AddHistoryEntry(int userID, HistoryType Historytype, int companyID)
{
// Do something...
}
public void AddHistoryEntry(int userID, HistoryType historyType, int companyID, string notes)
{
// Do something...
}
}
To this...
public class HistoryEntry
{
public long UserID { get; private set; }
public HistoryType HistoryType { get; private set; }
public long CompanyID { get; set; }
public string Notes { get; set; }
public HistoryEntry(long userID, HistoryType historyType)
{
this.UserID = userID;
this.HistoryType = historyType;
}
}
public class Utility
{
public void AddHistoryEntry(HistoryEntry entry)
{
// Do something...
}
}
}
Now, this is much better code design and is an Uncle Bob favourite. However, my colleague argues that it is so much more expensive to new-up an object every time we want to call this method.
Is he correct?
Further Explanation
Thanks for all the responses so far. I left out many details in the hope of brevity, but some of them have caused issues with the answers.
- Utility class doesn't exist. I just wanted to put the methods in a class for you all to see. In actuality it's in a sensible class.
- There were, in fact, 5 AddHistoryEntry methods in the original code. All of which took a lot of int parameters. One reason for the refactoring was that
AddHistory(0, 1, 45, 3);
doesn't really tell you much! - AddHistoryEntry is not called from a tight loop, but it is widely used throughout the application.
Corrections
I've now updated the code examples as I had made a mistake with some of the parameters.
回答1:
He might be correct if you have millions of these objects in memory simultaneously. But if you don't, then he's bringing up what is almost certainly a moot point. Always choose a better design first, and then modify it only if you're not meeting performance requirements.
回答2:
Creating a new object is very cheap. You'd have to create a lot of objects in a very tight loop to notice any difference... but at the same time, it's not free. You also need to bear in mind that the costs are half hidden - you have some cost upfront when you create the object, but also it means the garbage collector has more work to do.
So is a cleaner design worth the performance hit? We can't tell you that - it depends on how your code is used. I strongly suspect the performance hit will be insignificant, but if you are using this in a tight loop, it may not be. There's only one way to find out though: measure it if you're concerned. Personally I'd probably just go for the cleaner design and only check the performance when it looked like it was becoming a problem.
(If this method is hitting disk or a database, by the way, the difference is almost bound to be insignificant.)
One suggestion, by the way: does your HistoryEntry
type actually need to be mutable? Could you make all the properties read-only, backed by private read-only variables?
Just to mention Will's point - I somewhat agree with him. If these two methods are the only places you need this concept, I'm not at all sure it's worth creating a new type for the sake of it. Not because of the performance aspect, but just because you're introducing an extra bunch of code for no demonstrated benefit. The key word here is demonstrated - if you're actually using the same collection of parameters in other places, or could usefully put logic into the HistoryEntry
class, that's a different matter entirely.
EDIT: Just to respond to the point about multiple integer arguments - C# 4 will allow named arguments which should make this easier. So you could call:
AddHistoryEntry(userId: 1, companyId: 10);
Making the names visible with an additional class needs one of:
- Named constructor arguments from C# 4 (in which case you're no better off than with the method)
- Mutable types (urgh - that could lead to hard to trace bugs which wouldn't have been in the original version)
- Long static method names to construct instances ("FromUserAndCompanyId") which become unwieldy with more parameters
- A mutable builder type
- A bunch of "With" methods:
new HistoryEntry().WithCompanyId(...).WithUserId(...)
- this makes it harder to validate at compile-time that you've provided all the required values.
回答3:
Why not a static method in the class itself?
public static void AddHistoryEntry(HistoryEntry entry)
Saves you the utility class. The new object may be expensive, it may not--but it probably doesn't matter. Good defaults allow you to extend your code much more easily, and you'll save more time in maintenance than you do in performance, unless you're doing this in a loop.
回答4:
You're both wrong, but he's more wrong than you.
回答5:
It's expensive relative to not creating objects in .NET, in the same way that 1 is a large number relative to 0. It all depends on your perspective. If this is in a loop that's being run 20 million times, I might worry; otherwise, I wouldn't care.
Having said that, I'd like to point out that Utility
is not a good name for a class, and refactoring into a HistoryEntry
also likely requires you to verify that the parameter being passed in is not null
, a requirement that you did not have originally when all parameters were value types.
It's also commonly considered an anti-pattern to have objects with data but no behaviour, which HistoryEntry
seems to be. Try to avoid doing this too often.
回答6:
It is not really expensive to create objects in .NET. In fact, object creation is very fast, even if you create millions of them.
However, garbage collection is not cheap. If you create a lot of short-lived objects, you may experience considerable slowdown when gc kicks in. It makes sense to avoid creating new short-lived objects if creation runs thousands or millions of times. If, for example, you create several objects with every network packet received by a server with thousands of connections.
That said, it is never wise to avoid calling new
before demonstrating that lots of objects really do slow down gc. In your example, they most probably would not.
回答7:
Is this really a performance bottleneck? If not, then it would be a clear case of premature optimization to avoid using objects in this case. Maintainability and design should take priority over performance until you start hitting significant performance issues.
回答8:
It is slightly more computationally expensive, but for that cost you get the benefit of cleaner, more conceptual code that can more quickly respond to future business demands. Performance vs agility.
回答9:
No. The garbage collector is optimized to be highly efficient when dealing with short-lived objects.
来源:https://stackoverflow.com/questions/2105011/is-it-expensive-to-create-objects-in-net