views:

21

answers:

2

Today something peculiar happened while debugging in VS 2008. I will give the small code snippet

List<IPageHandler> myPageList = TaskSOM.PageList;

if( myPageList != null && myPageList.Count > 0 )
{
     PageHandler aPage = myPageList[0] as PageHandler;
     ...; // Some more code below  
}

While running the application the typecast failed and aPage became null (That was the reason for debugging). So all the code that was using that vaiable failed. But during debug the first element in the myPageList was in deed a PageHandler. When I execute the line in immediate window

  PageHandler aPage = myPageList[0] as PageHandler;

aPage variable has proper value. But If move the debugger to that line and execute I get a null. Due to confidentiality I couldn't share the whole code. But has anyone faced such an issue with the immediate window in the past. Is there any material regarding how the immediate window works.

+1  A: 

This would be a very good example of code where you don't want to use the as operator. Clearly you cannot afford the cast to fail or you would have included a null test and did something meaningful if the cast failed.

Use a real cast. You'll get an informative exception that gives you a much better hint why the cast failed:

 PageHandler aPage = (PageHandler)myPageList[0];

Exceptions are your friend, don't avoid them. Taking a wild guess: this could happen when you use a COM object in a thread and the COM server doesn't support marshaling. If that's the case then the exception message will tell you so.

Hans Passant
Thanks for the quick reply. I found the problem. I will add another post to give the full details.
ferosekhanj
I have given the answer for this question. But still I dont understand how the same type cast works in immediate window.
ferosekhanj
Assembly.LoadFile() should only *ever* be used if you write a special assembly dumping tool. Use LoadFrom().
Hans Passant
A: 

So here is the full details. The exception was

[A]SimpleClassLib.PageHandler cannot be cast to [B]SimpleClassLib.PageHandler. Type A originates from 'SimpleClassLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' in the context 'LoadNeither' at location 'D:...\bin\SimpleClassLib.dll'. Type B originates from 'SimpleClassLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' in the context 'Default' at location 'D:...\bin\Debug\SimpleClassLib.dll'

The developer mentioned [A]D:...\bin\SimpleClassLib.dll in one of the application config file and built the real app with [B]D:...\bin\Debug\SimpleClassLib.dll so one part of the application created PageHandler instance from [A] and filled the list and the other part was trying to type cast to PageHandler from [B].

The following example will trigger this error easily. Hope this helps someone. This is the simple class library. Build this as a dll.

// SimpleClassLib.dll    
namespace SimpleClassLib
    {
        public class Foo
        {
            string Prop1 { get { return "I am Foo!!"; } }
        }
    }

The following is the console app. The app links to the SimpleClassLib like normal add reference from VS 2008. Also it loads an instance from another path.

// Separate console application App.exe
// Progoram.cs
using SimpleClassLib;
namespace App
{
  class Program
  {
            List<object> myFooList;
            Program()
            {
                myFooList = new List<object>();
                Assembly a = Assembly.LoadFile(@"<differentpath>\SimpleClassLib.dll");
                Type aFooType = a.GetType("SimpleClassLib.Foo");
                ConstructorInfo aConstructor = aFooType.GetConstructor(new Type[] { });
                myFooList.Add(aConstructor.Invoke(new object[]{}));
                myFooList.Add(aConstructor.Invoke(new object[] { }));
                myFooList.Add(aConstructor.Invoke(new object[] { }));
            }

            void DumpPeculiar()
            {
                for (int i = 0; i < myFooList.Count; i++)
                {
                    // If one inspects the list in debugger will see a list of
                    // Foo but this Foo comes from a different load context so the
                    // following cast will fail. While if one executes the line
                    //  f = myFooList[i] as Foo
                    // it will succeed
                    Foo f = myFooList[i] as Foo;
                    Foo f1 = (Foo)myFooList[i];
                }
            }

            static void Main(string[] args)
            {
                Program p = new Program();
                p.DumpPeculiar();
            }
      }
}
ferosekhanj