Off-topic: A C# lesson learned about conditional operators

  • Post category:Off-topic
  • Post comments:7 Comments
  • Reading time:3 mins read

If it was hard to write, it should be hard to understand, that’s an unwritten rule-that-rules-them-all of programming. You absolutely love to apply syntactical stunts to impress your coworkers, especially if you do C# and they don’t, don’t you?

One of those stunts (at least from C/AL) perspective is a C-language type common feature known as conditional operator. It allows you to write this:

a = b ? c : d;

when you would normally (in C/AL, for example) get more eloquent:

if (b == true)
{
    a = c;
} else {
    a = d;
}

This (b == true) could have been replaced with just (b), but I put it there for clarity.

But! (There is always a “but”!)

Even though it may not be obvious, there is a catch 22 which often doesn’t cause any issues at all, but sometimes can break everything. It’s the fact that the whole expression involving the conditional operator, just like any other expression, has a type. It means one type.

While you may think this is a nice syntactical stunt:

public object ConvertBoxedIntToProperIntType(object a) 
{
    return Convert.ToInt64(a) > Int32.MaxValue ? Convert.ToInt64(a) : Convert.ToInt32(a);
}

 

This won’t achieve anything at all, and the boxed type of the returned value will always be Int64, simply because the expression type involved is higher of the two types – and that’s Int64.

If you are one of the types that have their opinion and don’t want to be confused by facts, take a look at this little fiddle: https://dotnetfiddle.net/6KzSpb

I’ve just stumbled upon this problem when deserializing Dictionary<string,object> from JSON, where any integer number would just be deserialized as Int64, so I wrote my nice JsonConverter class that attempted to be too smart by applying conditional operator to the return value:

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{    
    var jsonValue = serializer.Deserialize<JValue>(reader);
    if (jsonValue.Type == JTokenType.Integer)
    {
        return jsonValue.Value<Int64>() > Int32.MaxValue ? jsonValue.Value<Int64>() : jsonValue.Value<Int32>();
    }
    return jsonValue.Value;
}

Alas, it didn’t work, so I had to fix it by changing it to less elegant (or geeky) into more direct, totally obvious:

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    var jsonValue = serializer.Deserialize<JValue>(reader);
    if (jsonValue.Type == JTokenType.Integer)
    {
        if (jsonValue.Value<Int64>() > Int32.MaxValue)
        {
            return jsonValue.Value<Int64>();
        }
        return jsonValue.Value<Int32>();
    }
    return jsonValue.Value;
}

And – so help me God – I swear if I didn’t write it myself, and I got by that code in a review for example, I’d routinely change this into my original, immediately causing a bug.

And yes – sorry for an off-topic post, but what’s really off-topic nowadays, when it comes to NAV? Winking smile

Vjeko

Vjeko has been writing code for living since 1995, and he has shared his knowledge and experience in presentations, articles, blogs, and elsewhere since 2002. Hopelessly curious, passionate about technology, avid language learner no matter human or computer.

This Post Has 7 Comments

  1. Carsten Scholling

    🙂

  2. Krisje

    I would go for
    return jsonValue.Value() > Int32.MaxValue ? jsonValue.Value() : jsonValue.Value;
    😉

    1. Krisje

      oops
      return jsonValue.Value() > Int32.MaxValue ? jsonValue.Value() : jsonValue.Value
      ;

      1. Krisje

        Hm, I’m messing up your post here, the is skipped 😛
        return jsonValue.Value () > Int32.MaxValue ? jsonValue.Value() : jsonValue.Value();

        Last attempt 😉

        1. Vjeko

          So you’d do exactly the same mistake I did originally 🙂

  3. Jason Down

    I believe this occurs because the conditional operator ignores what is being assigned to on the left (initially) and focuses on the right side of the expression (then it checks to make sure the expression return type is compatible with what is being assigned to). The two pieces on the right must be able to evaluate to the same type. In this case, you require an implicit conversion between the int32 (int) type and int64 (long) type to return the same type. When we consider this, only the int32 -> int64 implicit conversion exists, so that is the type that is returned.

Leave a Reply