

C#: No, you don’t get to use the “as” operator any more - Indyan
http://badcodemonkey.wordpress.com/2011/04/29/no-you-dont-get-to-use-the-as-operator-any-more/

======
snprbob86
Many language designers, including many on Microsoft's Managed Language teams,
consider null reference and invalid cast exceptions to be huge blunders.

The "Nice" language shows that it's pretty easy to statically avoid this:
<http://nice.sourceforge.net/safety.html>

The sad fact is that the Java and .NET base class libraries were designed
before (1) generics and (2) a generic "Nullable" (aka "Option") type were
available. Anyone designing a statically typed environment today should
certainly include both and design a compiler to enforce their usage and
safety.

~~~
ssp
The main problem with non-nullable types is that the static analysis involved
doesn't work for object fields., so you can't write code like this:

    
    
            if (object.some_field != null)
                   object.some_field.foo();
    

because the compiler is unable to prove that it's correct. Instead you have to
do a clumsy workaround like this:

    
    
        SomeType some_local;
    
        some_local = object.some_field;
        if (some_local != null)
            some_local.foo();

~~~
Groxx
That seems like it would be a trivial thing to do in an early compilation
step... why is the workaround necessary?

~~~
ssp
The problem is that if the code looks like this:

    
    
            if (object.some_field != null)  {
                   some_unrelated_call();
                   object.some_field.foo();
            }
    

proving that some_unrelated_call() didn't set object.some_field to null is
very difficult.

~~~
kenjackson
In _general_ , it's also difficult to prove that 'object' doesn't get set to
null too.

~~~
ssp
Depends on what you mean by "general", I suppose, but if 'object' is a local
variable in a Java-ish language, then there is a fairly straightforward data
flow analysis that can do a good conservative estimate.

~~~
kenjackson
_but if 'object' is a local variable in a Java-ish language, then there is a
fairly straightforward data flow analysis that can do a good_

But if it's not, and/or if it can be adress taken...

------
mendicant
Yes, null refs are bad.

But so is an InvalidCastException -- Which is what your new code could throw.

~~~
ondrasej
I guess the point of the article is that when you're doing an invalid cast,
you in fact want to get an InvalidCastException, not NullPointerException.

~~~
iramiller
Actually you don't want to ever use exception driven development. The use of
the as operator is a simple metadata check. Throwing exceptions in .Net is
expensive.

Just because I felt the need to check my own facts (Friday afternoon?) I wrote
a simple console app with two classes Class1 and Class2 : Class1. Using a
Stopwatch object I checked the elapsed ticks for an 'as' an 'is' and an
invalid cast exception check.

'as' = 5 ticks

'is' = 4 ticks

'invalid cast' = 147990 ticks

Note this was just enough test to validate my assumption and that I never
condone my developers taking an optimize first development approach... But
there is still a right and a wrong way to do the job sometimes and exception
driven development is not the right way.

~~~
masklinn
> Actually you don't want to ever use exception driven development. The use of
> the as operator is a simple metadata check. Throwing exceptions in .Net is
> expensive.

it doesn't matter, it's throwing an exception when the caller code is broken.
That's ok, it's an error.

------
jasonkester
It's best if you think about the reason that "as" was added to C#. It's so you
can do this:

    
    
      Customer c = person as Customer;
      if (c != null)
      {
        // do something
      }
      else
      {
        // not a customer.  must be just a regular person. do something else
      }
    

instead of this:

    
    
      if (person is Customer)
      {
        Customer c = (Customer)person;
        // do something
      }
      else
      {
        // not a customer.  must be just a regular person. do something else
      }
    

... and that's it. It's just somebody's idea of a little cleaner syntax, not
having to cast twice to get where you want to be.

In that light, the author's second example is closest to correct. It's just
missing the "else" bit to handle the case where the base object you get passed
is not be the exact type you're looking for.

------
singular
EDIT: Actually, yes, _ahem_ , I see - if e.NewItem is actually null you're
going to get confused between what the problem actually is here, and that
might not be repro. Perhaps I posted a little too soon... but assuming you
check for that separately, the point stands :)

I have to disagree here - I prefer the initial code example. It's cleaner, and
as soon as you get to the line of code in the debugger you'll know exactly
what's up regardless of the on-the-surface-misleading null exception. So what
if it's initially confusing?

In any case both examples are wrong, and I don't think that's made clear
enough - you should be _actively_ looking to check for exceptions, not
passively accepting that they might happen.

As an aside, I've always found it cleaner to say:-

    
    
        var cust = e.NewItem as Customer;
        if(cust == null)
            throw new BlahException("blah blah blah");
    
        cust.Save();
    

Compared to, say:-

    
    
        if(!(e.NewItem is Customer))
            throw new BlahException("blah blah blah");
    
        var cust = (Customer)e.NewItem;
        cust.Save();
    

(Note that in the second case that a thread could jump in and spoil your day
anyway, not to mention that .NewItem could be some crazy property which
randomly decides to return null in the second case, and you'd get the same old
nasty exception, as mentioned in other posts)

So I think the 'as' operator has value in this alone as handy sugar.

Obviously the real problem is that null exceptions happen at all, or that
anything actually returns null, and by God is the Option discriminated union
in F# a joy (union of Some/None) after dealing with all that shizzle.

------
noblethrasher
I would make the public version of Save a static method on an abstract class
(or a static method in a utility class) and avoid the whole worry over null.
It's only a bit more work up front.

    
    
        abstract class Foo
        {
            public static Result Save(Foo foo)
            {
                if(foo != null)
                   foo._Save();
                else
                {
                    //Do Stuff...
                }
    
            }
    
            protected abstract Result _Save();
        }
    
    

Then use like so,

    
    
        foreach(Foo s in xs)
        {
            Foo.Save(s); //or s.Save() if Save() is an extension method.
        }

~~~
jbrechtel
This misses the author's point. In his example the bug you want to detect is
that the referenced object in question is not a Customer (or a Foo in your
example).

In your else block you can't report anymore information than the
NullPointerException in the author's original example reported.

If you still use an 'as' operator at the callsite then you've lost all useful
information. If you do a cast then you'll at least get an exception with WHAT
it was you were trying to cast.

~~~
noblethrasher
I don’t think that I’ve missed the author's point; but any time I see an if
statement and polymorphic code used near each other, I question whether or not
the programmer understands OOP. His example is not _exactly_ using polymorphic
code, but he is basically dispatching based on the runtime type.

We know that a method that accepts parameters that are reference types should
check to see if they are null. Since instance methods cannot check their first
parameter (the this pointer), we make it impossible for outside code to call
instance methods. Now callers never have to worry about the runtime type of an
object or whether or not it’s null.

If you’re going to circumvent the type system with explicit casts then, of
course, you’re going to get invalid cast exceptions, but that’s for the client
code to deal with.

------
scottdw2
Actually... he misses a big use case (stolen from haskell)... Using extension
methods (which accept null receivers) and "as" you can get pretty readable
straight line code... something like:

    
    
        name = (obj as Customer).LiftName() ?? "Guy Incognito"
    

Interestingly, built in support for this (w/o the need to write a "LiftName"
extension method) almost made it into VB 2008, but it ended up getting cut.

------
singular
There are subtleties to it too -
[http://blogs.msdn.com/b/ericlippert/archive/2009/10/08/what-...](http://blogs.msdn.com/b/ericlippert/archive/2009/10/08/what-
s-the-difference-between-as-and-cast-operators.aspx)

:-)

------
tomjen3
I tend to use it because it makes it reading from left to right feel right
instead of jumping back and forth.

And really, don't give me null objects unless I specifically say I can handle
them.

------
InclinedPlane
((e.NewItem as Customer) ?? Customer.NullObject).Save();

Edit: The idea being, you have a sub-class of Customer which implements the
Null Object pattern, which you instantiate through a static member on Customer
or a Property method or what-have-you. The great thing about this is that you
can tailor the behavior in this situation to whatever you want. Want to throw
an exception? Sure, throw whatever exception you want from
CustomerNullObject.Save(). Want to log an error and otherwise silently fail?
No prob, easy peasy.

------
k2xl
strange... ecma script allows it...

