An "is" operator puzzle, part one


It is possible for a program with some local variable x:

bool b = x is FooBar;

to assign true to b at runtime, even though there is no conversion, implicit or explicit, from x to FooBar allowed by the compiler! That is,

FooBar foobar = (FooBar)x;

would not be allowed by the compiler in that same program.

Can you create a program to demonstrate this fact? This is not a particularly hard puzzle but it does illustrate some of the subtleties of the “is” operator that we’ll discuss in the next episode.

Comments (21)

  1. Stuart says:

    This can happen if FooBar is a type parameter to the method containing the code and x is a variable of a known type that isn't known to be convertible to FooBar, right?

    something like:

    void Frob<FooBar>() {

     int x = 1;

     bool b = x is FooBar;

    }

    Frob<int>();

  2. Kyle Trauberman says:

    We're having a fun time trying to solve this in the C# StackOverflow chat room.  Thanks for the puzzle Eric!

  3. Bradley says:

    bool b = (object)x is (object)FooBar?

  4. Bradley says:

    Gah, Ignore that, I misread the problem.

  5. Michael says:

    static void M<T>(T x)

           {

               bool b = x is FooBar;

               FooBar foobar = (FooBar)x;

           }

  6. Brian says:

    Michael beat me too it, but here is a example you can just dump into LINQ Pad if you want to play:

    void Main()

    {

    var x = new FooBar();

    CheckForFoobar<FooBar>(x);

    }

    public class FooBar

    {

    public int test;

    }

    public static bool CheckForFoobar<T>(T x)

    {

    bool b = x is FooBar;

    //Will not compile

    //FooBar foobar = (FooBar)x;

    b.Dump("b");

    return b;

    }

  7. Daniel Holder says:

    void Main()

    {

    dynamic x = new FooBar();

    bool b = x is FooBar;

    Console.WriteLine(b);

    x = null;

    /* Uncomment this line to create compiler error: Cannot convert null to 'FooBar' because it is a non-nullable value type. */

    //FooBar foobar = (FooBar)x;

    }

    public struct FooBar

    {

    }

  8. Daniel Holder says:

    I, too, tested my code in LINQPad. Just realized that it does not produce a compiler error, but a RuntimeBinderException exception at runtime.

  9. John Kerr says:

    What if FooBar is abstract? Then "is" can succeed, while instance creation can't.

  10. Rob P says:

    This seems more like a subtlety of explicit conversions than a subtlety of the 'is' operator, since the 'is' operator is translated to an IL 'isinst' instruction under the hood.  So I took a minute to look it up in the spec, and the spec even admits to it being a subtlety:

    "The above rules do not permit a direct explicit conversion from an unconstrained type parameter to a non-interface type, which might be surprising. The reason for this rule is to prevent confusion and make the semantics of such conversions clear."

    This is because type arguments can't be substituted at compile-time, to know what explicit conversions exist.  The allowable conversions for the 'is' operator, however, are all evaluated after type arguments have been substituted.  

  11. Dan Tao says:

    I think any developer who's attempted generic math has run into this:

    public void DoMath<T>(T value)

    {

       if (value is int)

       {

           DoIntMath((int)value);

       }

    }

  12. vernarim@libero.it says:

    Hello Eric.

    My guess is the below one, although I think you meant something smarter.

    namespace NS

    {

     using Bad = FooBar;

     class FooBar

     {

       static void Main()

       {

         Bad x = new Bad();

         bool b = x is FooBar;

         Console.WriteLine(b);  //returns true

       }

     }

    }

    Cheers

  13. vernarim@libero.it says:

    Nope, forget it…it's an alias, thus the type is the same, and any cast/conversion would be allowed.

  14. LukeC says:

    var x = new TypedReference();

    bool b = x is object;

    Console.WriteLine(b);

    object o = (object)x;

  15. Mike says:

    Maybe this?

       class Program

       {

           static void Main(string[] args)

           {

               dynamic x = new FooBar();

               bool b = x is FooBar;

               Console.WriteLine(b);

               Console.ReadLine();

           }

           class FooBar

           {

           }

       }

  16. Luke says:

    Eric, I can not wait to see a correct answer. Who is that lucky guy who guessed?

  17. Tezt says:

    int x = 0;

    bool b = x is FooBar;

    Console.WriteLine(b);

    FooBar foobar = (FooBar)x;

  18. Francisco Ruiz says:

    namespace IsConsoleApplication

    {

       class Program

       {

           static void Main(string[] args)

           {

               var x = new object();

               var b = x is FooBar;

               var fooBar = (FooBar)x;

           }

           class FooBar

           {

           }

       }

    }

    'object' is compatible with 'FooBar', so b = true; but 'x' don't refer to a 'FooBar' when the type verification is performed in the type hierarchy.

  19. Byzod says:

    Agree with Francisco Ruiz. 'is' operator is kind of compatible check, but not 'typeof x == "foobar"' thing

  20. DanyR_ says:

    I agree with Stuart (comment #1).

  21. Anon says:

    >…

    > var x = new object();

    > var b = x is FooBar;

    >…

    > 'object' is compatible with 'FooBar', so b = true;

    If b would indeed be true, I would need to fix a lot of code…

Skip to main content