7

UPDATE: I've found the answer, which I will post in a couple of days if nobody else does.


I am creating a numeric struct, so I am overloading the arithmetical operators. Here is an example for a struct that represents a 4-bit unsigned integer:

public struct UInt4
{
    private readonly byte _value;

    private const byte MinValue = 0;
    private const byte MaxValue = 15;

    public UInt4(int value)
    {
        if (value < MinValue || value > MaxValue)
            throw new ArgumentOutOfRangeException("value");

        _value = (byte) value;
    }

    public static UInt4 operator +(UInt4 a, UInt4 b)
    {
        return new UInt4((a._value + b._value) & MaxValue);
    }
}

The overloaded addition operator allows this code:

var x = new UInt4(10);
var y = new UInt4(11);
var z = x + y;

Here, the calculation overflows, so the variable z has the value 5. I would also like to be able to do this, however:

var x = new UInt4(10);
var y = new UInt4(11);
var z = checked ( x + y );

This sample should throw an OverflowException. How can I achieve that?

I have already established that the checked context does not extend to called methods, so, for example, this does not throw, regardless of whether it is called in a checked or unchecked context:

public static UInt4 operator +(UInt4 a, UInt4 b)
{
    int i = int.MaxValue;

    //this should throw in a checked context, but when
    //the operator is used in a checked context, this statement
    //is nonetheless unchecked.
    byte b = (byte)i;

    return new UInt4((a._value + b._value) & MaxValue);
}

Is there a way of declaring two overloads of the addition operator, one checked and the other unchecked? Alternatively, is there a way to determine at run time the context of the caller (which seems highly unlikely, but I thought I'd ask nonetheless), something like this:

public static UInt4 operator +(UInt4 a, UInt4 b)
{
    byte result = (byte)(a._value + b._value);

    if (result > MaxValue)
        if (ContextIsChecked())
            throw new OverflowException();
        else
            result &= MaxValue;
     return new UInt4(result);
}

private static bool ContextIsChecked()
{
    throw new NotImplementedException("Please help.");
}
phoog
  • 39,474
  • 5
  • 73
  • 110
  • Here is an answer http://stackoverflow.com/questions/2873765/how-to-find-out-the-current-overflow-checking-context that says it is more or less impossible... Waiting to see your approach... – Alexei Levenkov Mar 16 '13 at 01:05
  • @AlexeiLevenkov the answer I found also indicates that it is impossible. One approach I've thought of would be to define `UInt4Checked` and `UInt4Unchecked` types with explicit conversions between them. – phoog Mar 16 '13 at 01:07
  • Downvoter, please explain. – phoog Mar 16 '13 at 01:43
  • Voted to close as not a real question. You clearly posted this to make a point, not to find the answer. Get a blog. – Guffa Mar 16 '13 at 10:16
  • @Guffa In fact, I posted this question to find an answer, and then 15 minutes after posting it, I had another idea about where to look for the answer, and I found it. Anyway, stackoverflow encourages people to post questions to which they already know the answer (even though that's not what happened here), and to answer them themselves. See http://meta.stackexchange.com/questions/12513/should-i-not-answer-my-own-questions – phoog Mar 21 '13 at 20:21

1 Answers1

2

According to MSDN, the checked and unchecked keywords only apply to the integral types. Therfore, you cannot create your own types that can make use of the checked and unchecked keywords.

Jacob VanScoy
  • 1,133
  • 6
  • 14
  • 1
    Close, so +1, but I am not going to accept this answer as it is because there's nothing on the first page that specifies "*built-in* integral types". I'm trying to define my own integral type, which is why I want it to be sensitive to checked/unchecked context. In other words, there should be an explicit link on the first page to the documentation for built-in integral types, but there is not, so I find the page ambiguous. I found an unambiguous source, and I will accept the first answer that cites it. – phoog Mar 16 '13 at 00:30
  • I think I got it. It's because you didn't override the implicit conversion operator and allow it to be converted to one of the built-in integral types like [this SO thread](http://stackoverflow.com/questions/7615113/define-custom-integer-based-type) suggests. Even though I can't find this explicitly stated anywhere on MSDN, I'm guessing it's similar to how switch statements work as explained by Eric Lippert at the bottom of [this thread](http://stackoverflow.com/questions/5154104/what-exactly-are-integral-types). – Jacob VanScoy Mar 16 '13 at 00:55
  • Oh no, that's not it at all. The correct answer is "you cannot create your own types...". Consider: If I created an implicit conversion operator to a built-in integral type, I would get overflow checking against the range of the built-in integral type, not the range of the type I'm trying to define. The example in the SO thread you link to won't roll over when it overflows in an unchecked context. – phoog Mar 16 '13 at 00:56
  • Or rather, it won't behave as it should when being converted in an unchecked context, where it should not be possible to throw an exception. – phoog Mar 16 '13 at 01:02
  • You're right. That wouldn't make any sense so it is restricted to the built-in integral types, which I and MSDN were both confusing with all integral types. – Jacob VanScoy Mar 16 '13 at 01:07
  • @phoog: how would C# know that your type is an "integral type". _Obviously_, "integral type" means "integral type that the compiler knows about", hence, "built-in integral type". – John Saunders Mar 16 '13 at 01:09
  • "'Integral' refers to integer types (i.e. whole numbers). In C# this means types like int, long, short, etc." -- Taken directly from [this thread](http://stackoverflow.com/questions/5154104/what-exactly-are-integral-types). – Jacob VanScoy Mar 16 '13 at 01:12
  • @JohnSaunders hypothetically, C# could have some way of allowing me to flag my type as an integral type; if it did, that would have been the answer to my question. – phoog Mar 16 '13 at 01:22
  • @phoog: if that hypothetical language existed, it would not be C#. – John Saunders Mar 16 '13 at 01:26
  • @JohnSaunders that has to be fallacious. – phoog Mar 16 '13 at 01:30
  • C# is a real language. You propose an imaginary language. The two sets are disjoint. – John Saunders Mar 16 '13 at 01:31
  • 1
    @JohnSaunders If we assume for the sake of argument that elephants can fly, we cannot deduce that the elephants in our hypothetical world are not elephants because real elephants in the real world cannot fly. Similarly, if someone asks you "will this bus number 10 take me to city hall?" it is far more helpful to answer "no" than to say "if that bus existed, it would not be this bus number 10". In 2003, I might have asked about using `sizeof(int)` in verifiable code, and you would have said "the language you propose is not C#". But now we *can* do that -- does that mean C# 5 is not C#? – phoog Mar 16 '13 at 01:40
  • @JohnSaunders did you downvote my question? What about the question makes it unclear, unuseful, or without research effort? – phoog Mar 16 '13 at 01:41
  • Okay, I decided to accept this answer despite my earlier objections; the only improvement I was holding out for was a reference to the C# specification, which is less ambiguous in that it explicity states that the `checked` and `unchecked` operators affect "predefined" operations, thus excluding operator overloading. – phoog Mar 21 '13 at 20:35