May 2007 - Posts

If there was ever a poll conducted for the most favorite C# operator, I'd guess the conditional operator ?:, also known as the ternary operator, will win hands down. I find it to be one of those tools that make things shorter and clearer at the same time. But how well do you know the operator?

What do you think is wrong with the following piece of code?

    interface IDoer {}

    class ADoer : IDoer { }
    class BDoer : IDoer { }
    class Program
    {
        static void Main(string[] args)
        {
            bool flag = bool.Parse(args[0]);
            IDoer doer = flag ? new ADoer() : new BDoer();
        }
    }

Looks straightforward, doesn't it? Yet, this does not compile; the error being

error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'ADoer' and 'BDoer'

The C# spec says that the two expressions that appear on either side of the : must be one of the following

  • Both of them should be of the same type
  • The first one should be convertible to the second
  • The second one should be convertible to the first

In the above case, ADoer and BDoer are both convertible to IDoer (because they implement IDoer), but because neither of them is implicitly convertible to the other, compilation fails. It doesn't matter to the compiler that the result of the expression is always convertible to IDoer, all it cares about is the deduction of the type of the ternary expression.

I guess this is one of those places where static typing gets in the way of the developer, instead of helping him. You win some, you lose some :)

Posted by Senthil | 1 comment(s)

I've been dabbling with IronPython for the last few days, trying to host it and use it as a scripting engine. I have to say that I'm impressed. The hosting API is incredibly simple to use and Mike Stall's blog served as a handy reference.

One thing I wanted my scripts to do was to access private and internal members of types in the hosting application. I found that the command line python interpreter (ipy.exe) had a -X:PrivateBinding switch to allow access to private and internal members using the <type>_<type>__memberName convention (which seems to be the standard python name mangling for private members).

However, I couldn't find an easy way to do that from the hosting application. It was time to download the source code and figure out how the command line option got translated in code. It turned out to be pretty simple, there is a static class Options, which has properties for most of the command line options. From the hosting application, all you have to do is set the corresponding property before initializing the PythonEngine.

void Initialize()
{
Options.PrivateBinding = true;
pythonEngine = new PythonEngine();
}

 

Posted by Senthil | with no comments
Filed under:

 MethodLogger is a macro I wrote that adds log statements at the beginning and ending of all methods in the current document in Visual Studio. Unzip the file, load it in Visual Studio using Tools->Macros->Load Macro Project, open the appropriate C# file and then run the LogMethodStartAndEnd macro. It transforms

class SomeClass
{
public void SomeMethod()
{
MessageBox.Show("Doodly doodly Doo");
}
}
to
class SomeClass
{
public void SomeMethod()
{
Console.WriteLine("Start of SomeClass.SomeMethod()");
try
{
MessageBox.Show("Doodly doodly Doo");
}
finally
{
Console.WriteLine("End of SomeClass.SomeMethod()");
}
}
}
The code, written in VB .NET (which seems to be the only supported language for writing macros?) uses Visual Studio's automation interface to get the code model for the currently open file. It then walks through all the methods, figures out the starting and ending points of each method and inserts the appropriate statements, with the most significant part being the method signature. It does a very basic check (finds the first non-empty, non comment line and checks if it's the same as the text to be logged) to make sure it doesn't modify methods it has already added logs to - when the user runs the macro twice, for example.

It should be fairly simple to change Console.WriteLine to something else - right clicking on LogMethodStartAndEnd and selecting Edit should take you to the Macro Editor IDE and lines 92, 93 contain the text to be inserted at the start and end of each method.

Posted by Senthil | 2 comment(s)
Filed under:

A new version of deadlock detector is up at Deadlock Detector^. This version is a partial rewrite, with a modified version of the algorithm to detect the object being locked. It now does a virtual execution of the IL, following through all control paths to find the locked object. There are a few issues with the execution logic, sometimes resulting in stack overflows and infinite loops. Any comments/suggestions/bug reports welcome.

Posted by Senthil | with no comments
Filed under:

I'm sure most of you know about BackgroundWorker, a new component in .NET 2.0, that allows UI code to run tasks asynchronously, without running into cross thread update issues. But what would happen if the thread that launches the background worker is not a UI thread i.e. it doesn't run a message pump?
The answer is quite surprising and caused a piece of working code like this


AutoResetEvent waitForWorker = new AutoResetEvent(false);
void Method()
{
BackgroundWorker worker = new BackgroundWorker();
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_Completed)
worker.RunAsync();
waitForWorker.WaitOne();
}

void worker_Completed(...)
{
waitForWorker.Set();
}

to stop working after making a tiny modification like this

class MyForm : Form {}

AutoResetEvent waitForWorker = new AutoResetEvent(false);
void Method()
{
MyForm f = new MyForm();
BackgroundWorker worker = new BackgroundWorker();
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_Completed)
worker.RunAsync();
waitForWorker.WaitOne();
}

void worker_Completed(...)
{
waitForWorker.Set();
}

This caused Method() to hang indefinitely, because worker_Completed never got to run. That makes sense, because the thread running method is currently blocked on a call to waitForWorker.WaitOne() and background worker would require that thread to be pumping messages to get worker_Completed to run on that thread. But how did it work before? The first code snippet also blocks on WaitOne(), yet worker_Completed does run and everything works.

This is where things get interesting. A peek into BackgroundWorker via Reflector revealed that it internally uses the new AsyncOperationManager class to deal with running its event handlers like ProgressChanged and RunWorkerCompleted on the thread that called RunAsync. AsyncOperationManager is a general class that uses a SynchronizationContext object to do its stuff and it's SynchronizationContext that does the heavy lifting of running code on different threads. To get an instance of SynchronizationContext, AsyncOperationManager tries to access the SynchronizationContext.Current property, which retrieves the corresponding instance for the current thread.

The default behavior of the SynchronizationContext class is to simply delegate requests to run code on other threads to the threadpool. There are subclasses of SynchronizationContext, like System.Windows.Forms.WindowsFormsSynchronizationContext, that customize the behavior to do something different. WindowsFormsSynchronizationContext, for example, uses Control.BeginInvoke internally to marshal code to run on the thread that called RunAsync.

Can you see the problem now? Instantiating an instance of a Form derived class caused the SynchronizationContext of the current thread to change to WindowsFormsSynchronizationContext, which would use Control.BeginInvoke. And that would not work unless the target thread is pumping messages. The first sample worked because there was no specific SynchronizationContext available, so an instance of the base SynchronizationContext class itself is created. That would simply call ThreadPool.QueueUserWorkItem to run the event handlers, which obviously doesn't require the target thread to be pumping.

The lesson - be wary of using BackgroundWorker from non-UI threads, the behavior might change depending on objects previously instantiated on the thread.

 

Posted by Senthil | 1 comment(s)