Are there any good reasons why ternaries in C# are limited? - c#

Fails:
object o = ((1==2) ? 1 : "test");
Succeeds:
object o;
if (1 == 2)
{
o = 1;
}
else
{
o = "test";
}
The error in the first statement is:
Type of conditional expression cannot be determined because there is no implicit conversion between 'int' and 'string'.
Why does there need to be though, I'm assigning those values to a variable of type object.
Edit: The example above is trivial, yes, but there are examples where this would be quite helpful:
int? subscriptionID; // comes in as a parameter
EntityParameter p1 = new EntityParameter("SubscriptionID", DbType.Int32)
{
Value = ((subscriptionID == null) ? DBNull.Value : subscriptionID),
}

use:
object o = ((1==2) ? (object)1 : "test");
The issue is that the return type of the conditional operator cannot be un-ambiguously determined. That is to say, between int and string, there is no best choice. The compiler will always use the type of the true expression, and implicitly cast the false expression if necessary.
Edit:
In you second example:
int? subscriptionID; // comes in as a parameter
EntityParameter p1 = new EntityParameter("SubscriptionID", DbType.Int32)
{
Value = subscriptionID.HasValue ? (object)subscriptionID : DBNull.Value,
}
PS:
That is not called the 'ternary operator.' It is a ternary operator, but it is called the 'conditional operator.'

Though the other answers are correct, in the sense that they make true and relevant statements, there are some subtle points of language design here that haven't been expressed yet. Many different factors contribute to the current design of the conditional operator.
First, it is desirable for as many expressions as possible to have an unambiguous type that can be determined solely from the contents of the expression. This is desirable for several reasons. For example: it makes building an IntelliSense engine much easier. You type x.M(some-expression. and IntelliSense needs to be able to analyze some-expression, determine its type, and produce a dropdown BEFORE IntelliSense knows what method x.M refers to. IntelliSense cannot know what x.M refers to for sure if M is overloaded until it sees all the arguments, but you haven't typed in even the first argument yet.
Second, we prefer type information to flow "from inside to outside", because of precisely the scenario I just mentioned: overload resolution. Consider the following:
void M(object x) {}
void M(int x) {}
void M(string x) {}
...
M(b ? 1 : "hello");
What should this do? Should it call the object overload? Should it sometimes call the string overload and sometimes call the int overload? What if you had another overload, say M(IComparable x) -- when do you pick it?
Things get very complicated when type information "flows both ways". Saying "I'm assigning this thing to a variable of type object, therefore the compiler should know that it's OK to choose object as the type" doesn't wash; it's often the case that we don't know the type of the variable you're assigning to because that's what we're in the process of attempting to figure out. Overload resolution is exactly the process of working out the types of the parameters, which are the variables to which you are assigning the arguments, from the types of the arguments. If the types of the arguments depend on the types to which they're being assigned, then we have a circularity in our reasoning.
Type information does "flow both ways" for lambda expressions; implementing that efficiently took me the better part of a year. I've written a long series of articles describing some of the difficulties in designing and implementing a compiler that can do analysis where type information flows into complex expressions based on the context in which the expression is possibly being used; part one is here:
http://blogs.msdn.com/ericlippert/archive/2007/01/10/lambda-expressions-vs-anonymous-methods-part-one.aspx
You might say "well, OK, I see why the fact that I'm assigning to object cannot be safely used by the compiler, and I see why it's necessary for the expression to have an unambiguous type, but why isn't the type of the expression object, since both int and string are convertible to object?" This brings me to my third point:
Third, one of the subtle but consistently-applied design principles of C# is "don't produce types by magic". When given a list of expressions from which we must determine a type, the type we determine is always in the list somewhere. We never magic up a new type and choose it for you; the type you get is always one that you gave us to choose from. If you say to find the best type in a set of types, we find the best type IN that set of types. In the set {int, string}, there is no best common type, the way there is in, say, "Animal, Turtle, Mammal, Wallaby". This design decision applies to the conditional operator, to type inference unification scenarios, to inference of implicitly typed array types, and so on.
The reason for this design decision is that it makes it easier for ordinary humans to work out what the compiler is going to do in any given situation where a best type must be determined; if you know that a type that is right there, staring you in the face, is going to be chosen then it is a lot easier to work out what is going to happen.
It also avoids us having to work out a lot of complex rules about what's the best common type of a set of types when there are conflicts. Suppose you have types {Foo, Bar}, where both classes implement IBlah, and both classes inherit from Baz. Which is the best common type, IBlah, that both implement, or Baz, that both extend? We don't want to have to answer this question; we want to avoid it entirely.
Finally, I note that the C# compiler actually gets the determination of the types subtly wrong in some obscure cases. My first article about that is here:
http://blogs.msdn.com/ericlippert/archive/2006/05/24/type-inference-woes-part-one.aspx
It's arguable that in fact the compiler does it right and the spec is wrong; the implementation design is in my opinion better than the spec'd design.
Anyway, that's just a few reasons for the design of this particular aspect of the ternary operator. There are other subtleties here, for instance, how the CLR verifier determines whether a given set of branching paths are guaranteed to leave the correct type on the stack in all possible paths. Discussing that in detail would take me rather far afield.

Why is feature X this way is often a very hard question to answer. It's much easier to answer the actual behavior.
My educated guess as to why. The conditional operator is allowed to succinctly and tersely use a boolean expression to pick between 2 related values. They must be related because they are being used in a single location. If the user instead picks 2 unrelated values perhaps the had a subtle typo / bug in there code and the compiler is better off alerting them to this rather than implicitly casting to object. Which may be something they did not expect.

"int" is a primitive type, not an object while "string" is considered more of a "primitive object". When you do something like "object o = 1", you're actually boxing the "int" to an "Int32". Here's a link to an article about boxing:
http://msdn.microsoft.com/en-us/magazine/cc301569.aspx
Generally, boxing should be avoided due to performance loses that are hard to trace.
When you use a ternary expression, the compiler does not look at the assignment variable at all to determine what the final type is. To break down your original statement into what the compiler is doing:
Statement:
object o = ((1==2) ? 1 : "test");
Compiler:
What are the types of "1" and "test" in '((1==2) ? 1 : "test")'? Do they match?
Does the final type from #1 match the assignment operator type for 'object o'?
Since the compiler doesn't evaluate #2 until #1 is done, it fails.

Related

Why isn't there an implicit typeof?

I think I understand why this small C# console application will not compile:
using System;
namespace ConsoleApp1
{
class Program
{
static void WriteFullName(Type t)
{
Console.WriteLine(t.FullName);
}
static void Main(string[] args)
{
WriteFullName(System.Text.Encoding);
}
}
}
The compiler raises a CS0119 error: 'Encoding' is a type which is not valid in the given context. I know that I can produce a Type object from its name by using the typeof() operator:
...
static void Main(string[] args)
{
WriteFullName(typeof(System.Text.Encoding));
}
...
And everything works as expected.
But to me, that use of typeof() has always seemed somewhat redundant. If the compiler knows that some token is a reference to a given type (as error CS0119 suggests) and it knows that the destination of some assignment (be it a function parameter, a variable or whatever) expects a reference to a given type, why can't the compiler take it as an implicit typeof() call?
Or maybe the compiler is perfectly capable of taking that step, but it has been chosen not to because of the problems that might generate. Would that result in any ambiguity/legibility issues that I cannot think of right now?
If the compiler knows that some token is a reference to a given type (as error CS0119 suggests) and it knows that the destination of some assignment (be it a function parameter, a variable or whatever) expects a reference to a given type, why can't the compiler take it as an implicit typeof() call?
First off, your proposal is that the compiler reason both "inside to outside" and "outside to inside" at the same time. That is, in order to make your proposed feature work the compiler must both deduce that the expression System.Text.Encoding refers to a type and that the context -- a call to WriteFullName -- requires a type. How do we know that the context requires a type? The resolution of WriteFullName requires overload resolution because there could be a hundred of them, and maybe only one of them takes a Type as an argument in that position.
So now we must design overload resolution to recognize this specific case. Overload resolution is hard enough already. Now consider the implications on type inference as well.
C# is designed so that in the vast majority of cases you do not need to do bidirectional inference because bidirectional inference is expensive and difficult. The place where we do use bidirectional inference is lambdas, and it took me the better part of a year to implement and test it. Getting context-sensitive inference on lambdas was a key feature that was necessary to make LINQ work and so it was worth the extremely high burden of getting bidirectional inference right.
Moreover: why is Type special? It's perfectly legal to say object x = typeof(T); so shouldn't object x = int; be legal in your proposal? Suppose a type C has a user-defined implicit conversion from Type to C; shouldn't C c = string; be legal?
But let's leave that aside for a moment and consider the other merits of your proposal. For example, what do you propose to do about this?
class C {
public static string FullName = "Hello";
}
...
Type c = C;
Console.WriteLine(c.FullName); // "C"
Console.WriteLine(C.FullName); // "Hello"
Does it not strike you as bizarre that c == C but c.FullName != C.FullName ? A basic principle of programming language design is that you can stuff an expression into a variable and the value of the variable behaves like the expression, but that is not at all true here.
Your proposal is basically that every expression that refers to a type has a different behaviour depending on whether it is used or assigned, and that is super confusing.
Now, you might say, well, let's make a special syntax to disambiguate situations where the type is used from situations where the type is mentioned, and there is such a syntax. It is typeof(T)! If we want to treat T.FullName as T being Type we say typeof(T).FullName and if we want to treat T as being a qualifier in a lookup we say T.FullName, and now we have cleanly disambiguated these cases without having to do any bidirectional inference.
Basically, the fundamental problem is that types are not first class in C#. There are things you can do with types that you can only do at compile time. There's no:
Type t = b ? C : D;
List<t> l = new List<t>();
where l is either List<C> or List<D> depending on the value of b. Since types are very special expressions, and specifically are expressions that have no value at runtime they need to have some special syntax that calls out when they are being used as a value.
Finally, there is also an argument to be made about likely correctness. If a developer writes Foo(Bar.Blah) and Bar.Blah is a type, odds are pretty good they've made a mistake and thought that Bar.Blah was an expression that resolves to a value. Odds are not good that they intended to pass a Type to Foo.
Follow up question:
why is it possible with method groups when passed to a delegate argument? Is it because usage and mentioning of a method are easier to distinguish?
Method groups do not have members; you never say:
class C { public void M() {} }
...
var x = C.M.Whatever;
because C.M doesn't have any members at all. So that problem disappears. We never say "well, C.M is convertible to Action and Action has a method Invoke so let's allow C.M.Invoke(). That just doesn't happen. Again, method groups are not first class values. Only after they are converted to delegates do they become first class values.
Basically, method groups are treated as expressions that have a value but no type, and then the convertibility rules determine what method groups are convertible to what delegate types.
Now, if you were going to make the argument that a method group ought to be convertible implicitly to MethodInfo and used in any context where a MethodInfo was expected, then we'd have to consider the merits of that. There has been a proposal for decades to make an infoof operator (pronounced "in-foof" of course!) that would return a MethodInfo when given a method group and a PropertyInfo when given a property and so on, and that proposal has always failed as too much design work for too little benefit. nameof was the cheap-to-implement version that got done.
A question you did not ask but which seems germane:
You said that C.FullName could be ambiguous because it would be unclear if C is a Type or the type C. Are there other similar ambiguities in C#?
Yes! Consider:
enum Color { Red }
class C {
public Color Color { get; private set; }
public void M(Color c) { }
public void N(String s) { }
public void O() {
M(Color.Red);
N(Color.ToString());
}
}
In this scenario, cleverly called the "Color Color Problem", the C# compiler manages to figure out that Color in the call to M means the type, and that in the call to N, it means this.Color. Do a search in the specification on "Color Color" and you'll find the rule, or see blog post Color Color.

Applying "is" operator to method group: why compiler allows it? [duplicate]

I'm interesting in some design choices of C# language.
There is a rule in C# spec that allows to use method groups as the expressions of is operator:
class Foo {
static void Main() { if (Main is Foo) Main(); }
}
Condition above is always false, as the specification says:
7.10.10 The is operator
• If E is a method group or the null literal, of if the type of E is a reference type
or a nullable type and the value of E is null, the result is false.
My questions: what is the purpose/point/reason of allowing to use the C# language element with no runtime representation in CLR like method groups inside the such "runtime" operator like is?
what is the purpose/point/reason of allowing to use the C# language element with no runtime representation in CLR like method groups inside the such "runtime" operator like is?
The language design notes archive make no mention of why this decision was made, so any guess at an answer will be a conjecture. They do mention that if the result of the "is" can be statically determined to always be true or false, that it be so determined and produce a warning. It seems plausible that this could simply be an error.
The most common reason for turning what could rightly be an error into a warning (or simply allowing it) is because that lessens the burden upon producers of programs that automatically generate code. However, I don't see a really compelling scenario here.
UPDATE:
I just checked the C# 1.0 specification. It does not have this language in it. It does not say anything about nulls or method group arguments. And of course it says nothing about method group conversions because in C# 1.0 there were no implicit method group conversions; you had to explicitly call "new D(M)" if you wanted to convert method M to delegate type D.
This latter point is justification for "M is D" returning false rather than true; You couldn't legally say "D d = M;" so why should "M is D" be true?
Of course in C# 2.0 this makes less sense, since you can say "D d = M;" in C# 2.0.
I also just asked one of the people present when the "is" operator was designed and he had no memory of ever deciding this question one way or the other. His suspicion was that the original design of the "is" operator was to not give any errors, only warnings, and that all the text in the spec about what to do with method groups and nulls and whatnot was added post-hoc, for the C# 2.0 version of the spec, and based on what the compiler actually did.
In short, it looks like this was a design hole in C# 1.0 that was papered over when the spec was updated for C# 2.0. It does not look like this specific behaviour was desired and deliberately implemented.
This theory is reinforced by the fact that anonymous methods do produce an error when used as an argument to "is" in C# 2.0. It would not be a breaking change to do so, but it would be a breaking change to make "M is D" suddenly start returning true or being an error.
FURTHER UPDATE:
While investigating this I learned something interesting. (To me.) When the feature was originally designed, the design was to allow either the name of a type, or a Type object as the right-hand argument to "is". That idea was abandoned well before C# 1.0 shipped though.
First of all a method is not a type, msdn clearly states the following:
The is operator is used to check
whether the run-time type of an object
is compatible with a given type
Example
public static void Test (object o)
{
Class1 a;
if (o is Class1) {}
}
From MSDN:
An is expression evaluates to true if both of the following conditions are met:
expression is not null.
expression can be cast to type. That is, a cast expression of the form (type)(expression) will complete without throwing an exception. For more information, see 7.6.6 Cast expressions.
So the reason for your example being false lands on the second point, it has to be castable to a specific type.
I hope I did not missunderstand the question.

Why do we have to use typeof, instead of just using the type?

When trying to assign a type to a property of type System.Type, why can't we do this?
foo.NetType = bool;
The compiler produces this warning:
"Expression expected."
The way to solve it is by doing this:
foo.NetType = typeof(bool);
My question is why can't we use the first approach? Isn't the compiler smart enough to figure out what we're trying to accomplish here? Is there some reason why we have to go with the second approach (typeof)?
Good question -- insofar as it is an interesting question in language design. This is maybe not an ideal question for this site, as it is not about specific, actual code.
It would be perfectly feasible to design a language in which a type name may be used as an expression without an explicit typeof operator.
Doing so would require a small number of extra rules and clarifications to be added to the language. For example, suppose we had:
enum Color { Red }
class Square
{
Color Color { get; set; }
void M()
{
Type t = Color.GetType();
In C# today this unambiguously means invoke the getter for property Color and call GetType on the result. (See the specification's "Color Color rule" section for an explanation of why.) In your proposed world there are three things this could mean:
Invoke the getter for Color, call GetType on the result, assign the Type object for Color to t.
Color results in a Type object for the type Color. Call GetType on that type, and assign the Type object for System.Type to t.
Color refers to the type Color, GetType refers to the non-static method, and this is an error because this is a static call to a non-static member.
You'd want to clarify the specification so that it was clear when Color in an expression meant the type and when it meant make a type object. So the proposed feature adds a small amount of ambiguity that must be dealt with, but it's totally doable. The language designers could come up with reasonable rules.
A language which allows a language element that is normally part of the compile-time analysis to instead be interpreted as code that creates an object that can be manipulated by code in the same program is called a homoiconic language. C# was a very nonhomoiconic language before C# 3; expression trees made C# much more homoiconic. C# 3 allow lambdas to be treated as program elements that are then used to generate methods that perform the action of the lambda body, but it also supports a homoiconic transformation from the lambda into an object that represents the body of the lambda.
One of the most homoiconic languages is Lisp; Lisp manipulates lists and any Lisp program can itself be though of as a list; it can be quoted and manipulated at runtime as objects rather than as code. So once again we see here the truth of the old joke about language design: every language designer eventually re-invents Lisp, badly.
I digress. Your question then is essentially: should C# be more or less homoiconic with respect to type names? Should a type name have one meaning -- a compile-time directive -- in contexts like
Foo x = new Foo();
object o = new List<Foo>[] {};
Foo.StaticMethod();
and have a very different meaning -- as the construction of an object that can be inspected at runtime in other contexts:
object t = Foo; // Assign a Type object to t.
?
Though it would certainly be possible to design a language like that, I don't much like it. Without an operator in there clearly calling out "hey, we are using what is normally a compile-time element in a homoiconic manner", it's potentially confusing. Before C# 3, there were no other homoiconic language features and so it would seem a bit strange to have the only one be that types could be used as both types or expressions that result in a Type object. And it does seem quite unfortunate that in some expressions it is unclear whether a particular simple name means "I'm using this type at compile time" or "I want to make an object".
Having an explicit typeof operator mitigates all these concerns; it is unambiguous when the type is being used homoiconically and very clear to the reader.
To address a very specific point about your question:
When trying to assign a type to a property of type System.Type...
C# does not generally speaking have special rules that apply only in assignments. The meaning of an expression is usually determined without appealing to the context in which the expression is being used. When we say:
x = y;
We don't generally say "Oh, I know that y is being assigned to x and x is of type X so I'm going to use that information in the analysis of y". Rather, the analysis goes from inside to outside: we work out what y means, and then decide whether or not it is compatible with X.
The exception to this rule is of course lambdas; lambdas do take into account their "target" type because the target type can be used to infer the types of an implicitly typed lambda. Getting these rules right was very complicated.
More generally, it's a bad idea to make assignment expressions special. There are lots of ways that values get assigned to variables:
M(x); // x is assigned to a formal
q = new [] { x }; // x is assigned to the first element of an array
y = new Y() { X = x }; // x is assigned to a property of Y.
You want the rules for all those assignments to be consistent; if an expression means "I'm a type object" then it should mean that in every context in which that expression can appear, not just as the right hand side of an assignment.
Sorry, I misunderstood your goal at first. However, you're still a bit confused.
You're attempting to assign an instance of a Type object to a property. bool is not an instance of the Type class, it is its own type. I appreciate that the terminology is a bit confusing, but they are two different things. That's why it doesn't work.
So here's a SWAG:
If I have a class:
public class Foo
{
public static int Bar = 1;
}
And I have some code like:
var foo = Foo;
You might say "ok, that definitely means the type"
Now if I have something like:
public class Bar
{
public void Foo ()
{
var hmm = Foo;
}
}
What do I mean? The type Foo? The method Foo? The namespace (if it existed) Foo?
By wrapping it in typeof, we make it explicit what we want. Also, it generates specific IL, but I'm assuming your question implicitly means "Why isn't the compiler smarter?"

Implicit conversion issue in a ternary condition [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Conditional operator cannot cast implicitly?
Why does null need an explicit type cast here?
I've had a search and haven't found a good explanation for why the following occurs.
I have two classes which have an interface in common and I have tried initializing an instance of this interface type using the ternary operator as below but this fails to compile with the error "Type of conditional expression cannot be determined because there is no implicit conversion between 'xxx.Class1' and 'xxx.Class2':
public ConsoleLogger : ILogger { .... }
public SuppressLogger : ILogger { .... }
static void Main(string[] args)
{
.....
// The following creates the compile error
ILogger logger = suppressLogging ? new SuppressLogger() : new ConsoleLogger();
}
This works if I explicitly cast the first conditioin to my interface:
ILogger logger = suppressLogging ? ((ILogger)new SuppressLogger()) : new ConsoleLogger();
and obviously I can always do this:
ILogger logger;
if (suppressLogging)
{
logger = new SuppressLogger();
}
else
{
logger = new ConsoleLogger();
}
The alternatives are fine but I can't quite get my head around why the first option fails with the implicit conversion error as, in my view, both classes are of type ILogger and I am not really looking to do a conversion (implicit or explicit). I'm sure this is probably a static language compilation issue but I would like to understand what is going on.
This is a consequence of the confluence of two characteristics of C#.
The first is that C# never "magics up" a type for you. If C# must determine a "best" type from a given set of types, it always picks one of the types you gave it. It never says "none of the types you gave me are the best type; since the choices you gave me are all bad, I'm going to pick some random thing that you did not give me to choose from."
The second is that C# reasons from inside to outside. We do not say "Oh, I see you are trying to assign the conditional operator result to an ILogger; let me make sure that both branches work." The opposite happens: C# says "let me determine the best type returned by both branches, and verify that the best type is convertible to the target type."
The second rule is sensible because the target type might be what we are trying to determine. When you say D d = b ? c : a; it is clear what the target type is. But suppose you were instead calling M(b?c:a)? There might be a hundred different overloads of M each with a different type for the formal parameter! We have to determine what the type of the argument is, and then discard overloads of M which are not applicable because the argument type is not compatible with the formal parameter type; we don't go the other way.
Consider what would happen if we went the other way:
M1( b1 ? M2( b3 ? M4( ) : M5 ( ) ) : M6 ( b7 ? M8() : M9() ) );
Suppose there are a hundred overloads each of M1, M2 and M6. What do you do? Do you say, OK, if this is M1(Foo) then M2(...) and M6(...) must be both convertible to Foo. Are they? Let's find out. What's the overload of M2? There are a hundred possibilities. Let's see if each of them is convertible from the return type of M4 and M5... OK, we've tried all those, so we've found an M2 that works. Now what about M6? What if the "best" M2 we find is not compatible with the "best" M6? Should we backtrack and keep on re-trying all 100 x 100 possibilities until we find a compatible pair? The problem just gets worse and worse.
We do reason in this manner for lambdas and as a result overload resolution involving lambdas is at least NP-HARD in C#. That is bad right there; we would rather not add more NP-HARD problems for the compiler to solve.
You can see the first rule in action in other place in the language as well. For example, if you said: ILogger[] loggers = new[] { consoleLogger, suppressLogger }; you'd get a similar error; the inferred array element type must be the best type of the typed expressions given. If no best type can be determined from them, we don't try to find a type you did not give us.
Same thing goes in type inference. If you said:
void M<T>(T t1, T t2) { ... }
...
M(consoleLogger, suppressLogger);
Then T would not be inferred to be ILogger; this would be an error. T is inferred to be the best type amongst the supplied argument types, and there is no best type amongst them.
For more details on how this design decision influences the behaviour of the conditional operator, see my series of articles on that topic.
If you are interested in why overload resolution that works "from outside to inside" is NP-HARD, see this article.
You can do that:
ILogger logger = suppressLogging ? (ILogger)(new SuppressLogger()) : (ILogger)(new ConsoleLogger());
When you have an expression like condition ? a : b, there must be an implicit conversion from the type of a to the type of b, or the other way round, otherwise the compiler can't determine the type of the expression. In your case, there is no conversion between SuppressLogger and ConsoleLogger...
(see section 7.14 in the C# 4 language specifications for details)
The problem is that the right hand side of the statement is evaluated without looking at the type of the variable it is assigned to.
There's no way the compiler can look at
suppressLogging ? new SuppressLogger() : new ConsoleLogger();
and decide what the return type should be, since there's no implicit conversion between them. It doesn't look for common ancestors, and even if it did, how would it know which one to pick.
Any time you change a variable of one type into a variable of another type, that's a conversion. Assigning an instance of a class to a variable of any type other than that class requires a conversion. This statement:
ILogger a = new ConsoleLogger();
will perform an implicit conversion from ConsoleLogger to ILogger, which is legal because ConsoleLogger implements ILogger. Similarly, this will work:
ILogger a = new ConsoleLogger();
ILogger b = suppress ? new SuppressLogger() : a;
because there is an implicit conversion between SuppressLogger and ILogger. However, this won't work:
ILogger c = suppress ? new SuppressLogger() : new ConsoleLogger();
because the tertiary operator will only try so hard to figure out what type you wanted in the result. It essentially does this:
If the types of operands 2 and 3 are the same, the tertiary operator returns that type and skips the rest of these steps.
If operand 2 can be implicitly converted to the same type as operand 3, it might return that type.
If operand 3 can be implicitly converted to the same type as operand 2, it might return that type.
If both #2 and #3 are true, or neither #2 or #3 are true, it generates an error.
Otherwise, it returns the type for whichever of #2 or #3 was true.
In particular, it will not start searching through all the types it knows about looking for a "least common denominator" type, such as an interface in common. Also, the tertiary operator is evaluated, and its return type deterimined, independant of the type of variable you are storing the result into. It's a two step process:
Determine the type of the ?: expression and calculate it.
Store the result of #1 into the variable, performing any implicit conversions as needed.
Typecasting one or both of your operands is the correct way to perform this operation if that's what you need.

Why ever cast reference types when you can use "as"? [duplicate]

This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
Casting: (NewType) vs. Object as NewType
In C#, why ever cast reference types when you can use "as"?
Casting can generate exceptions whereas "as" will evaulate to null if the casting fails.
Wouldn't "as" be easier to use with reference types in all cases?
eg:
MyObject as DataGridView
rather than,
(DataGridView)MyObject
Consider the following alternatives:
Foo(someObj as SomeClass);
and:
Foo((SomeClass)someObj);
Due to someObj being of the wrong type, the first version passes null to Foo. Some time later, this results in a NullReferenceException being thrown. How much later? Depends on what Foo does. It might store the null in a field, and then minutes later it's accessed by some code that expects it to be non-null.
But with the second version, you find the problem immediately.
Why make it harder to fix bugs?
Update
The OP asked in a comment: isn't is easier to use as and then check for null in an if statement?
If the null is unexpected and is evidence of a bug in the caller, you could say:
SomeClass c = someObj as SomeClass;
if (c == null)
{
// hmm...
}
What do you do in that if-block? There are two general solutions. One is to throw an exception, so it is the caller's responsibility to deal with their mistake. In which case it is definitely simpler to write:
SomeClass c = (SomeClass)someObj;
It simply saves you writing the if/throw logic by hand.
There is another alternative though. If you have a "stock" implementation of SomeClass that you are happy to use where nothing better is available (maybe it has methods that do nothing, or return "empty" values, etc.) then you could do this:
SomeClass c = (someObj as SomeClass) ?? _stockImpl;
This will ensure that c is never null. But is that really better? What if the caller has a bug; don't you want to help find bugs? By swapping in a default object, you disguise the bug. That sounds like an attractive idea until you waste a week of your life trying to track down a bug.
(In a way this mimics the behaviour of Objective-C, in which any attempt to use a null reference will never throw; it just silently does nothing.)
operator 'as' work with reference types only.
Sometimes, you want the exception to be thrown. Sometimes, you want to try to convert and nulls are OK. As already stated, as will not work with value types.
One definite reason is that the object is, or could be (when writing a generic method, you may not know at coding-time) being cast to a value type, in which case as isn't allowed.
One more dubious reason is that you already know that the object is of the type in question. Just how dubious depends on how you already know that. In the following case:
if(obj is MyType)
DoMyTypeStuff((MyType)obj);
else
DoMoreGeneralStuff(obj);
It's hard to justify using as here, as the only thing it really does is add a redundant check (maybe it'll be optimised away, maybe it won't). At the other extreme, if you are half-way to a trance state with the amount of information you've got in you're brain's paged-in memory and on the basis of that you are pretty sure that the object must be of the type in question, maybe it's better to add in the check.
Another good reason is that the difference between being of the wrong type and being null gets hidden by as. If it's reasonable to be passing in a string to a given method, including a null string, but it's not reasonable to pass in an int, then val as string has just made the incorrect usage look like a completely different correct usage, and you've just made the bug harder to find and potentially more damaging.
Finally, maybe if you don't know the type of the object, the calling code should. If the calling code has called yours incorrectly, they should receive an exception. To either allow the InvalidCastException to pass back, or to catch it and throw an InvalidArgument exception or similar is a reasonable and clear means of doing so.
If, when you write the code to make the cast, you are sure that the cast should work, you should use (DataGridView)MyObject. This way, if the cast fails in the future, your assumption about the type of MyObject will cause an invalid cast exception at the point where you make the cast, instead of a null reference exception at some point later.
If you do want to handle the case where MyObject is not a DataGridView, then use as, and presumably check for it being null before doing anything with it.
tl;dr If your code assumes something, and that assumption is wrong at run-time, the code should throw an exception.
From MSDN (as (C# reference)):
the as operator only performs reference conversions and boxing conversions. The as operator cannot perform other conversions, such as user-defined conversions, which should instead be performed using cast expressions.
Taking into consideration all of the comments, we came across this just the other day and wondered why you would do a direct cast over using the keyword as. What if you want the cast to fail? This is sometimes the desirable effect you want from a cast if you're casting from a null object. You then push the exception up the call stack.
So, if you want something to fail, use a direct cast, if you're okay with it not failing, use the as keyword.
As is faster and doesn't throw exceptions. Therefore it is generally preferred. Reasons to use casts include:
Using as, you can only assign types that are lower in the inheritance tree to ones that are higher. For example:
object o = "abc" as object;
DataGridView d = "abc" as DataGridView // doesn't do anything
DataGridView could create a custom cast that does allow this. Casts are defined on the target type and therefore allow everything, as long as it's defined.
Another problem with as is that it doesn't always work. Consider this method:
IEnumerable<T> GetList<T>(T item)
{
(from ... select) as IEnumerable<T>
}
This code fails because T could also be a Value Type. You can't use as on those because they can never be null. This means you'll have to put a constraint on T, while it is actually unnecesary. If you don't know whether you're going to have a reference type or not, you can never use as.
Of course, you should always check for null when you use the as keyword. Don't assume no exceptions will be thrown just becase the keyword doesn't throw any. Don't put a Try {} Catch(NullReferenceException){} around it, that't unneccesary and bloat. Just assign the value to a variable and check for null before you use it. Never use it inline in a method call.

Categories

Resources