Operator overloading and different types

前端 未结 5 1507
轮回少年
轮回少年 2021-01-04 09:33

I have a class Score which is going to be heavily used in comparisons against integers. I was planning on overloading the == operator to enable these comparisons as per th

相关标签:
5条回答
  • 2021-01-04 09:56

    "Is this a sensible use of operator overloading?"

    If it makes your code easier to understand and doesn't cause problems, I would say, yes, absolutely! There may also be other ways, but if this works for you I don't see an issue.

    "Should I be providing overloads for the LH and RH sides of the operators to allow the usage to be symmetrical?"

    I would argue that unless you have a specific reason to do so, in other words if you are using or need them, then you aren't gonna need it. (YAGNI) The only real exception I see is if you are writing a framework where you have a pretty good idea that someone else is gonna need it.

    0 讨论(0)
  • 2021-01-04 10:02

    I do think this is a strange situation to use an operator overload, but it is your call.

    However, my main point is that if you overload == you will also be required to overload !=

    If you then overload !=, the part where you compare x to check that it is not null using x != null will cause, the == operator to call the != operator. This isn't a problem in itself, as long as this doesn't then use a == comparison, as you will have a recursive set of calls, leading to a stack-overflow.

    However since a lot of people when overloading != implement it as 'not ==' - in your case this will cause a stack overflow.

    Solution: particularly in overloading ==, != and Equals(), its best to use use Object.ReferenceEquals(x, null); when comparing to null.

    0 讨论(0)
  • 2021-01-04 10:08

    tl;dr In the grand scheme of things; no, it's not a sensible idea.

    1. The fact that one feels the need to ask or search for this question is already telling.
    2. The fact that few people seem to care indicate that there might be a way to simply avoid the situation where you need to ask the question to begin with.
    3. These last two points should at least compel you to reconsider a few things, however unrelated they might seem to be.

    Consider assigning types to all inputs to your software component as soon as possible and outside of your domain logic. This way, what we commonly call the "domain model" never has to worry about this.

    In your case, that means wrapping all scores in Score objects. Because that's what they are. The need to make exceptions is always at least dubious and qualifies as a code smell.

    The next step is to view your "domain model" as nothing special -- just another set of modules. See all those (too often nasty) I/O interfaces that use tangible primitives as fulfilling a distinct/separate role. This will naturally (and almost magically) reduce the individual complexity of all your components (e.g. classes).

    One side-effect of this is that you might discover this very question to become nonsensical. "Why would I ever need to compare a score to a primitive integer if I'm not even sure that integer is a score? And if I'm sure, then why didn't I say so before (e.g. by boxing it)?"

    Going even further, you might realize many of your algorithms don't even need to know how a score is kept: it's likely that the majority of them only need to be able to compare them to see if they are equal or to see which is higher. I'm not saying the flexibility of modifying how you keep score will be useful in the future, but as a rule if it shouldn't matter then there's no need to tie code to an implementation just for being cute. Perhaps surprisingly, that also applies to primitives such as integers, and even the very concept of a number (which you don't need to care about in all cases where all you need is to compare two scores). The SOLID principles provide a good guide until you can develop intuitions for yourself.

    Counter: One can always bring the performance argument to argue against this line of thinking. In my experience, I found the JIT will optimize and inline whatever comes in the way of performance (especially with trivial things like equality comparisons) and when it doesn't there's almost always a way to abstract performance-critical code in a sane way. Don't hesitate to seal your types if dispatching is an issue.

    After measuring and finding a bottleneck in a correct design that can't be fixed by an alternative but equally sane design, then one needs to be honest about the fact that they hit a language limitation, in which case one should either consider extracting that code and writing it in a more appropriate language or hack around with the existing language but comment the hell out of that code.

    To conclude, I'll mention that I actually did exactly what you suggested in the past, and I can only be grateful that the code in question never got into production. Do it once and it doesn't seem harmful, but do it all over the place and that uneasy feeling settles in and only generates frustration until you finally realize why it's wrong (per the considerations above). If you're curious, I did this for types that represented entity keys that almost always wrapped a single primitive. Again, it seemed like a sensible idea and it might still seem like it to you, but I strongly believe it isn't now even though I wouldn't hear about it before. Maybe it's one of those things you need to burn yourself with before it finally sinks in.

    0 讨论(0)
  • 2021-01-04 10:16

    I might go ahead and define an implicit conversion from int to Score, so that when you deal with equality, you only need to deal with a single type.

    public static implicit operator Score(int value)
    {
        return new Score { Value = value }; // or new Score(value);
    }
    // define bool operator ==(Score score1, Score score2)
    
    // elsewhere 
    Score score = new Score { Value = 1 };
    bool isScoreOne = (score == 1);
    

    And while you're defining your own == operator, do remember to go ahead and define !=, and override Equals and GetHashCode.

    0 讨论(0)
  • 2021-01-04 10:16

    Usually when I see an overloaded operator, it comparisons of that class to itself. So if you have multiple instance variables, you would figure out how to compare the two to determine if something was equal, greater, less, etc.

    I don't see why you don't just do:

    if(myScore.value == 5)
    {
     //do stuff
    }
    
    0 讨论(0)
提交回复
热议问题