Sunday, January 23, 2011

C# Static Members' Not-So-Famous features

In this post we will be discussing some known but not-so-famous features of static members in C#. It is possible that you might be already aware of these special features.

Static Members and Application Domains:
Application domains are provided in a .net process to provide intra-process isolation. This isolation also includes the isolation of static members. Static members are static (shared) within the context of an application domain. It includes static fields, classes and constructors. As we know that:

Static Constructor is executed only once for a type

Now lets update this knowledge:

Static constructor is executed only once for a type PER APPLICATION DOMAIN

Let's consider Student class. This class only has a single static member MessageOfTheDay.

class Student
{
public static string MessageOfTheDay = "Default Message";
}

We create a Window as follows:

<Window x:Class="WpfApplication_Generic_Static.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid>
<Label Content="Message Of The Day" Height="27" HorizontalAlignment="Left" Margin="8,0,0,0"
Name="labelMessage" VerticalAlignment="Top" Width="200" FontWeight="Bold" />
<TextBox Height="231" HorizontalAlignment="Left" Margin="8,23,0,0"
Name="textBoxMessage" VerticalAlignment="Top" Width="481" />
<Button Content="Demonstrate static variables across AppDomains" Height="34" HorizontalAlignment="Left" Margin="6,265,0,0"
Name="btnSetMessage" VerticalAlignment="Top" Width="292" Click="btnSetMessage_Click" />
</Grid>
</Grid>
</Window>

The code behind of the Window is as follows:

public partial class MainWindow : Window
{
AppDomain appDomain;
public MainWindow()
{
InitializeComponent();
appDomain = AppDomain.CreateDomain("MyNewDomain");
}

private void btnSetMessage_Click(object sender, RoutedEventArgs e)
{
Student.MessageOfTheDay = this.textBoxMessage.Text;
appDomain.DoCallBack(displayMessage);
displayMessage();

}

private static void displayMessage()
{
string message = string.Format("AppDomain Name: {0}\n\nMessage of the Day: {1}",
AppDomain.CurrentDomain.FriendlyName, Student.MessageOfTheDay);

MessageBox.Show(message);
}
}

As you can see this Window has a single text box and a button. Clicking the button handles the Click event of the button using btnSetMessage_Click handler. The handler assigns a new value to MessageOfTheDay static member of Student class. This also executes displayMessage callback in a different application domain appDomain and shows the value of Student.MessageOfTheDay in this application domain. In order to prove that the current domain still holds the text entered in the textbox, we are showing the value [in the Base AppDomain] of Student.MessageOfTheDay. Let's run this and enter some text in the TextBox. Let's Enter "Muhammad Siddiqi".



So after clicking the button there would be two message boxes displayed. Can you guess the messages in those message boxes?

Based on the above discussion, the following message boxes are displayed:





This proves our point. Although we have set Student.MessageOfTheDay in the current domain but the change is not visible in the other domain as they are isolated.

Static Member initialization and Static constructors:
If we are setting the value of a static field in initializer and static member. What value would it hold? The answer is whatever would be executing last would be resulting in the value of static field. In a previous post, we have discussed about this:



In words: Initializers are executed from child class to base class. The same is true for the value assignment to the constructors in the inheritance hierarchy. The constructors, themselves, are executed from Base class to child class. Additionally, the constructors are always executed when both initializers are value assignment is finished. So in the case of static members also, the static initializer is always executed before static constructor. Based on this discussion, you would always find the value of a static field as set in the static constructors when you would use it first time if they are set in both static initializer and static constructor.

Let's have a class named Banner as follows:

static class Banner
{
public static string MessageOfTheDay = "Default Banner Message";

static Banner()
{
MessageOfTheDay = "Default Banner Message from static constructor";
}
}

Since Banner has only static members, we can declare itself as static. Now if we execute the following code, you can guess what should be displayed:

private void btnBannerMessage_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(Banner.MessageOfTheDay);
}

This results in the following MessageBox:



This is exactly as we discussed above.

Generics and their Static Members:
As we know that for a Generic type, The whole new set of classes are generated for generic classes for each specialized usage by JIT compiler at runtime. That is why static members in a Generic are not shared across different type of TypeMembers. They are only shared within similar TypeMembers. Here TypeMember:
class Classlt;TypeMember>
{
TypeMember StaticField;
}

Let us declare a generic class MyItem as follows:

class MyItem <T>
{
public static T Item { get; set; }
}

Now we use this class as follows:

private void btnGenericItem_Click(object sender, RoutedEventArgs e)
{
//int type for Generic Type
MyItem<int>.Item = 1;

//decimal type for Generic Type
MyItem<decimal>.Item = 2.3m;

//Showing message box for verification
MessageBox.Show(
string.Format("MyItem<int>.Item : {0}, MyItem<decimal>.Item : {1}",
MyItem<int>.Item.ToString(), MyItem<decimal>.Item.ToString()));
}

Can you guess what would be the contents of the MessageBox?

Now verify if it matches from actual Message box.



As you can see that both specific type generated for Generic holds different values of static members, so they are not shared.

Summary:
As a summary of this discussion:

1. Static members are not shared across application domain.
2. Static constructors are always executed after static initializers.
3. Static members are not shared across different specific type generated for Generics.

Download:

No comments: