1

Why is the compiler not able to automatically convert the values in this expression properly?

var input = "Hello";
object x = string.IsNullOrEmpty(input) ? input : DBNull.Value;

//could try this too and get similar compile time error
object x2 = string.IsNullOrEmpty(input) ? 1 : input;

I understand that DBNull.Value cannot be cast to a string; However, I'm curious as to why it cannot be coalesced into an object because the result is just storing a reference. If you place (object) in front of DBNull.Value it will compile and run just fine.

Matt Phillips
  • 11,249
  • 10
  • 46
  • 71
  • All of the answers except for gdoron's are missing the point. This isn't a "hey help me make this compile" question. It's more asking why the c# compiler isn't capable of or chooses not to be able to look at the type of the variable being assigned to versus the types of the variables in the 2nd and 3rd parameters. – Matt Phillips May 15 '12 at 01:59
  • Found this answer to a slightly unrelated question http://stackoverflow.com/a/2215959/186359 I think it basically proves that you can't add information (the type of the assignment variable) when evaluating an expression. I think this might be the real answer – Matt Phillips May 15 '12 at 04:18

3 Answers3

3

It is because string is not castable to DbNull and vice-versa. When using the ternary operator, both resultant operands must be compatible.

Matthew
  • 24,703
  • 9
  • 76
  • 110
  • I would sum it with _"There need to be an implicit conversion from one of the types to the other."_ – gdoron May 14 '12 at 21:12
  • @gdoron there's no explicit conversion either. – Matthew May 15 '12 at 01:46
  • I get that part already. What I'm wondering is why it's not possible (feasible) for the compiler to look at the type being assigned to (object) and realize hey both these types have an implicit conversion to object. Forget about what they have to each other. – Matt Phillips May 15 '12 at 01:57
  • @MattPhillips: That's simply not the way the language works. Even while it might look simple here, consider other situations where you're calling an overloaded method, where there may be various possible conversions available. It's much simpler if each expression has its own type, identified entirely separately from how it's used. There are some exceptions to this, such as lambda expressions and the null literal, but they add complexity to the language rules - so those situations are kept to the minimum. – Jon Skeet May 15 '12 at 07:47
2

You can fix it with:

string x = string.IsNullOrEmpty(input) ?
                                       input :
                                       DBNull.Value.ToString();

I have found this excellent explanations in Eric Lippert's blog post on Type inference woes:

The specification for the ?: operator states the following:

The second and third operands of the ?: operator control the type of the conditional expression. Let X and Y be the types of the second and third operands. Then,

  • If X and Y are the same type, then this is the type of the conditional expression.

  • Otherwise, if an implicit conversion exists from X to Y, but not from Y to X, then Y is the type of the conditional expression.

  • Otherwise, if an implicit conversion exists from Y to X, but not from X to Y, then X is the type of the conditional expression.

  • Otherwise, no expression type can be determined, and a compile-time error occurs.

In this case:

  • string and DBNull aren't the same type.
  • string doesn't have an implicit conversion to DBNull
  • DBNull doesn't have an implicit conversion to string

So we end up with a compile-time error.

The compiler doesn't check what is the type that can "hold" those two types.

KyleMit
  • 30,350
  • 66
  • 462
  • 664
gdoron
  • 147,333
  • 58
  • 291
  • 367
  • 1
    That makes sense. I think what I was looking for was the `implicit` part. Kinda makes you wonder why it doesn't try and do implicit conversion to the type of the variable they are assigning to. If I'm storing to object I don't care what the actual type is since I'm just storing a reference to it anyways. – Matt Phillips May 15 '12 at 01:56
  • @MattPhillips. Then you found it... :) – gdoron May 15 '12 at 06:55
2

Help the compiler find the common base type you want, like this:

object x = string.IsNullOrEmpty(input) ? (object)input : DBNull.Value;
Jeppe Stig Nielsen
  • 60,409
  • 11
  • 110
  • 181