Such funding without funding than trying to what most free downloadable music free downloadable music online lending process i simple and completely? Where borrowers to triple digit interest will movies movies receive bad and addresses.

Home > Black Belt, C# > Black Belt C# Series – Language Keywords

Black Belt C# Series – Language Keywords

April 13th, 2009

The Black Belt C# series aims to uncover powerful but lesser-known features of the C# language. Each article introduces a few of these features and shows you how to use them to take your programming to the next level.

 

The “as” Keyword

The as operator is a quick way to try to cast an object to a specific type. The operator returns null if the cast fails, instead of raising an exception.

There are cases where you may have an incoming object that may be a specific type. Take a look at this X10 (home automation) example:

 

   1: private class X10Device { }
   2: private class DoorSensor : X10Device { }
   3: private class LightSwitch : X10Device { public bool On { get; set; } }
   4:  
   5: private void Controller_DeviceChangedState(X10Device Source)
   6: {
   7:     //...respond to device change based on type
   8: }

 

In this example, imagine an X10 controller is wired up to our DeviceChanged…() method and it’s called every time an x10 device changes state. Any type of X10 device could be passed into the method and we can’t be sure of the exact type of device until we check for it at runtime.

Lets say that we only want to pay attention to light switches and ignore the other device types. There are a few ways we could attempt to single out light switches and start working with them.

Here’s a traditional way of doing this:

   1: private void Controller_DeviceChangedState(X10Device Source)
   2: {
   3:  
   4:     if (Source is LightSwitch)
   5:     {
   6:         LightSwitch SourceLightSwitch = (LightSwitch)Source;
   7:  
   8:         if (SourceLightSwitch.On == true)
   9:         {
  10:             MessageBox.Show("Light Turned On!");
  11:         }
  12:  
  13:     }
  14:  
  15: }

 

This example uses the “is” keyword to single out light switches. Then it creates a new variable that is certainly a light switch and wires it up to the Source reference knowing with full certainty that the cast will succeed.

 

Here’s an alternative way to accomplish the same thing, by using the “as” keyword:

   1: private void Controller_DeviceChangedState(X10Device Source)
   2: {
   3:  
   4:     LightSwitch SourceLightSwitch = Source as LightSwitch;
   5:  
   6:     if (SourceLightSwitch != null)
   7:     {
   8:         if (SourceLightSwitch.On == true)
   9:         {
  10:             MessageBox.Show("Light Turned On!");
  11:         }
  12:     }
  13:  
  14: }

If you ask me, this example is a little bit more succinct and readable. It also neatly groups together the case for Source being null or a different type (such as a DoorSensor).

 

If you can afford a slightly lower readability, the “as” statement also allows for a more compressed syntax where the cast attempt and check are performed all on the same line:

   1: LightSwitch SourceLightSwitch = null;
   2:  
   3: if ((SourceLightSwitch = Source as LightSwitch) != null)
   4: {
   5:     if (SourceLightSwitch.On == true)
   6:     {
   7:         MessageBox.Show("Light Turned On!");
   8:     }
   9: }

 

 

The “using” Keyword

The using keyword is an unusual case of two separate (but related) keywords rolled into one name.

Aliases (the using directive)

Aliases can save you a lot of time when you have oft-used complex generics and namespaces. The using directive allows you to define the composition of these types in one place instead of typing them out over and over again. Here’s an example with nested dictionaries:

 

   1: using ComplexDictionary = Dictionary<int, Dictionary<string, List<string>>>;
   2:  
   3: public partial class Main : Form
   4: {
   5:     ComplexDictionary MainComplexDictionary = new ComplexDictionary();
   6:     ComplexDictionary SecondaryComplexDictionary = new ComplexDictionary();
   7:  
   8:     public Main()
   9:     {
  10:         InitializeComponent();
  11:     }
  12:  
  13: }

As you can see, the using directive safely abstracts the “ComplexDictionary” type to a single location and is easily readable. Changing the ComplexDictionary type would be a simple task, no matter how many times it’s used throughout the code. Furthermore, creating further new ComplexDictionary variables is now a snap. Here’s the ugly alternative:

   1: public partial class Main : Form
   2: {
   3:     Dictionary<int, Dictionary<string, List<string>>> MainDictionary = 
   4:         new Dictionary<int, Dictionary<string, List<string>>>();
   5:  
   6:     Dictionary<int, Dictionary<string, List<string>>> SecondaryDictionary = 
   7:         new Dictionary<int, Dictionary<string, List<string>>>();
   8:  
   9:     public Main()
  10:     {
  11:         InitializeComponent();
  12:     }
  13:  
  14: }

 

 

Explicit Object Scope (the using statement)

The using statement is a bulletproof way to use disposable objects. Here’s an example:

   1: using (Form NewForm = new Form())
   2: {
   3:     NewForm.Enabled = true;
   4:     NewForm.StartPosition = FormStartPosition.CenterScreen;
   5:     NewForm.Size = new Size() { Height = 100, Width = 100 };
   6:     NewForm.ShowDialog();
   7: }

In this example, a new form is created and displayed and execution blocks at ShowDialog() until the form is closed. As soon as the form is closed by the user NewForm.Dispose() is called and NewForm goes out of scope - automatically.

This is functionally equivalent to the following code:

   1: {
   2:     Form NewForm = new Form();
   3:  
   4:     try
   5:     {
   6:         NewForm.Enabled = true;
   7:         NewForm.StartPosition = FormStartPosition.CenterScreen;
   8:         NewForm.Size = new Size() { Height = 100, Width = 100 };
   9:         NewForm.ShowDialog();
  10:     }
  11:     finally
  12:     {
  13:         if (NewForm != null) ((IDisposable)NewForm).Dispose();
  14:     }
  15:  
  16:  
  17: }

The using statement makes it easy to contain object scope and ensure that objects are disposed properly every time. Taking advantage of the using statement is a great way to improve quality while not sacrificing readability.

 

The “checked” and “unchecked” Keywords

The checked and unchecked keywords are useful for controlling what happens when an arithmetic operation overflows. By default, .NET projects created in Visual Studio will allow arithmetic overflow to happen silently and without warning.

 

Default Overflow Example – Outputs “-2” with default project configuration
   1: //32,767 is the maximum value for a short 
   2: short FirstValue = 32767;
   3: short SecondValue = 32767;
   4:  
   5: //Outputs -2
   6: Debug.WriteLine((short)(FirstValue + SecondValue));

 

image Global overflow checking is not turned on by default because it’s a significant performance hit and arithmetic overflow is not really a common occurrence.

You can turn on overflow checking by opening up your project advanced build settings (left) and checking the appropriate box but it’s generally a better practice to selectively enable overflow checking instead.

These two keywords were created to give developers just that precision level of control needed over overflow checking. To use the checked keyword, simply wrap your math statement inside a “Checked { }” block.

 

Tip:

The “Checked” and “Unchecked” keywords only apply to code directly in the specified block and do not extend to arithmetic in nested function calls. Calling a function from within your checked block will not cause overflow checking to occur inside the called function.

 

Checked Overflow Example – Raises Exception
   1: //32,767 is the maximum value for a short 
   2: short FirstValue = 32767;
   3: short SecondValue = 32767;
   4:  
   5: checked
   6: {
   7:     //Raises Overflow Exception
   8:     Debug.WriteLine((short)(FirstValue + SecondValue));
   9: }

Any overflows occurring inside the parenthesis will safely raise an OverflowException, failing loudly:

 

image

 

Correspondingly, if you do enable global overflow checking, you can selectively disable it by wrapping statements inside an “Unchecked { }” block. This is especially useful for reaping performance benefits in tight loops where you can prove that overflow will not occur. Again, unchecked { } will only have performance benefits if you have manually enabled overflow checking.

Unchecked Example – Assumes global overflow checking manually enabled
   1: unchecked
   2: {
   3:     //Index will never exceed 3,276.
   4:     for (int Index = 0; Index <= 3276; Index++)
   5:     {
   6:         //Will never overflow – Index * 10 will never exceed 32,760
   7:         Debug.WriteLine((short)(Index * 10));
   8:     }
   9: }

 

 

How does overflow checking work?

Overflow checking is effectively added into your project at compile time. Turning on global overflow checking or using checked { } blocks causes the compiler to generate slightly different IL to accommodate your request. Here’s the IL from the previous example of adding two shorts:

 

Unchecked - Adds FirstValue and SecondValue
   1: .maxstack 2
   2: .locals init (
   3:     [0] int16 FirstValue,
   4:     [1] int16 SecondValue)
   5: L_0000: nop 
   6: L_0001: ldc.i4 0x7fff
   7: L_0006: stloc.0 
   8: L_0007: ldc.i4 0x7fff
   9: L_000c: stloc.1 
  10: L_000d: nop 
  11: L_000e: ldloc.0 
  12: L_000f: ldloc.1 
  13: L_0010: add 
  14: L_0011: conv.i2 
  15: L_0012: box int16
  16: L_0017: call void [System]System.Diagnostics.Debug::WriteLine(object)
  17: L_001c: nop 
  18: L_001d: nop 
  19: L_001e: ret 

 

Checked – Adds FirstValue and SecondValue with Overflow Check
   1: .maxstack 2
   2:  .locals init (
   3:      [0] int16 FirstValue,
   4:      [1] int16 SecondValue)
   5:  L_0000: nop 
   6:  L_0001: ldc.i4 0x7fff
   7:  L_0006: stloc.0 
   8:  L_0007: ldc.i4 0x7fff
   9:  L_000c: stloc.1 
  10:  L_000d: nop 
  11:  L_000e: ldloc.0 
  12:  L_000f: ldloc.1 
  13:  L_0010: add.ovf 
  14:  L_0011: conv.ovf.i2 
  15:  L_0012: box int16
  16:  L_0017: call void [System]System.Diagnostics.Debug::WriteLine(object)
  17:  L_001c: nop 
  18:  L_001d: nop 
  19:  L_001e: ret 

 

As you can see, the checked block doesn’t radically alter the output IL. Can you spot the overflow check? In the checked (second) example, the compiler emitted “add.ovf” on line 13 instead of “add”. That’s pretty much it (apart from another check on line 14 for an implicit conversion).

The special IL “add.ovf” performs an overflow check before moving the result onto the stack. Intermediate language also contains similar versions for subtraction (sub.ovf), multiplication (mul.ovf), and numeric conversion.

Share/Save/Bookmark

Robert Black Belt, C# , , , , ,

  1. No comments yet.
  1. June 30th, 2010 at 11:30 | #1
  2. June 30th, 2010 at 22:06 | #2