Feb 21 David | .NET, C#, Microsoft

C# Features Review, Part 1

After seeing Microsoft to release Visual Studio 2010 RC version I have decided again to look at C#/.NET new and older versions just to look what C#/.NET can give us to solve problems. Today I am going to start looking at some C# language features what I find more interesting parts. In one or more posts I will try to go through all of the C# 4.0 features in coming weeks.

Anonymous Types

Anonymous Types were introduced in C# 3.0 and is supported on 3.5+ .NET. It allows us to create objects with read-only properties and we don't have to define new type for it. Let's look at example.

// Creating Anonymous Type
var rgbColor = new 
{ 
    red = 255, 
    blue = 255, 
    green = 255, 
    description = "White Color" 
};

Console.WriteLine("Red: {0}, Blue: {1}, Green: {2} <-- {3}", 
    rgbColor.red, rgbColor.blue, rgbColor.green, rgbColor.description);
// Red: 255, Blue: 255, Green: 255 <-- White Color
Console.ReadKey(true);

In this example we created anonymous types with 4 properties. We used var statement which tells compiler to use type of the object from the right side. In this case this is anonymous type, but it also works with built-in or user defined types. We listed properties using objects and collections initializer, which I am going to look more deeply a bit later. Let's go to investigate how actually anonymous types do work.

Visual Studio does handle anonymous type properties too:

Point[] points = new Point[] { new Point(1, 1), new Point(2, 2), new Point(3, 3) };

var sqrPoints =
    from point in points
    select new { X = Math.Pow(point.X, 2), Y = Math.Pow(point.Y, 2) };

foreach (var point in sqrPoints)
{
    Console.WriteLine("X: {0}, Y: {1}", point.X, point.Y);
}
// X: 1, Y: 1
// X: 4, Y: 4
// X: 9, Y: 9
Console.ReadKey(true);

In this example we create 3 points array and we using LINQ to create new collection of points but these points are going to have they X and Y in square. When you are using LINQ to go through records where each record might have dozen of parameters we can only take those we want. Let's say in this example we could have taken only X or Y in our anonymous type. It's much nicer to work with smaller objects and memory footprint should be lower too. 

var rgbColor2 = new
{
    red = 255,
    blue = 255,
    green = 255,
    description = "White Color"
};

Console.WriteLine("Type: {0}", rgbColor2.GetType().ToString());
// Type: <>f__AnonymousType0`4[System.Int32,System.Int32,System.Int32,System.String]
Console.WriteLine("Type: {0}", rgbColor.GetType().ToString());
// Type: <>f__AnonymousType0`4[System.Int32,System.Int32,System.Int32,System.String]
Console.ReadKey(true);

Now we are getting to know how C# handles those types. If anonymous objects have identical count of properties, properties types and the order of those properties are the same then those anonymous objects have the same type. Remember this, because this is going to help us later.

object color = (object)rgbColor;
Console.WriteLine(color);
// { red = 255, blue = 255, green = 255, description = White Color }
var againColor = DynamicCast(color, new { red = 0, blue = 0, green = 0, description = "" });
Console.WriteLine("Red: {0}, Blue: {1}, Green: {2} <-- {3}",
    againColor.red, againColor.blue, againColor.green, againColor.description);
// Red: 255, Blue: 255, Green: 255 <-- White Color
Console.ReadKey(true);
// Tomas Petricek, MVP
private static T DynamicCast(object value, T type)
{
    return (T)value;
}

Here is interesting example. All anonymous objects are limited to method scope. This means that we can't pass it outside our method without some additional work. The only way to pass this anonymous object outside our method is to cast it to the object. This is the easy part, it's a bit more complicated to make it usable again. Tomas Petricek (MVP) has posted working solution for this. In the example before we find out that we can define identical anonymous types, this method is based on that idea. We create anonymous object and pass it's type to DynamicCast, DynamicCast function casts given object to this type and return it. The only problem is that Microsoft does not recommend passing anonymous object outside your function, it just brakes strong typing. I would suggest following this rule. This is just workaround that does work, but you should never be used.

Console.WriteLine("rgbColor  HashCode: {0}", rgbColor.GetHashCode());
Console.WriteLine("rgbColor2 HashCode: {0}", rgbColor2.GetHashCode());
//rgbColor  HashCode: 2113248095
//rgbColor2 HashCode: 2113248095

if (rgbColor.Equals(rgbColor2))
{
    Console.WriteLine("They are equal.");
}
else
{
    Console.WriteLine("They are not equal.");
}
// They are equal.
Console.ReadKey(true);

In this last example I wanted to show you that if types is the same for two anonymous objects with same count of properties, types and order of properties, Equals() and GetHashCode() works too. As you can see rgbColor and rgbColor2 anonymous objects has the same type and we can compare them using Equals() method.

MSDN Reading

http://msdn.microsoft.com/en-us/library/bb397696.aspx [Anonymous Types (C# Programming Guide)]

Object and Collection Initializers && Auto-Implemented Properties

Let's briefly look how those two features works in C# by looking at the examples. This is going to be mixed example.

class PlotPoint
{
    private int _someInnerValue = 0;

    // This is auto-implemented properties
    public double X { get; set; }
    public double Y { get; set; }
    public string Title { get; set; }
    
    // Additional auto-implemented properties
    public double Length { get; }
    public bool SetOnlyProperty { set; }

    // standard property
    public int Something
    {
        get
        {
            return -this._someInnerValue;
        }
        set
        {
            this._someInnerValue = value * 2;
        }
    }
}

This is out special class for the next example. This class demonstrates how to you can use properties. First three properties are auto-implemented with ability to set and get them without writing the body for setter and getter while the next two shows that you can define just one of them. In short words, "set;" or "get;" will be auto-implemented properties. This is the shortest way to define getter and setter.

var point = new PlotPoint() { X = 10, Y = 10 };
var point2 = new PlotPoint() { X = 0, Y = 0, Title = "Hello Point", Something = 10, SetOnlyProperty = true };
var point3 = new PlotPoint() { Title = "Start Coord" };
List list = new List() { 0, 1, 2, 3, 4, GiveMeFive(), 6, 7, 7 + 1, 7 + 2 };
List list2 = new List()
{
    new PlotPoint() { X = 0, Y = 0, Title = "Start Point" },
    null, // Point to compute
    null, // Point to compute
    new PlotPoint() { X = 1, Y = 1, Title = "Some Middle Point" },
    null, // Point to compute
    null, // Point to compute
    new PlotPoint() { X = 2, Y = 2, Title = "End Point" }
};
public static int GiveMeFive()
{
    return 5;
}

This one demonstrates how to use object and collection initializers. In the first three lines we initialize PlotPoint objects and using initializer we define some properties. You can set as many properties you need to. Each property can only be set once in the initializer list. The last two objects are lists. Here we combine both object and collection initializers. It is allowed to use null, other methods, operations and etc. in those lists.

MSDN Reading

http://msdn.microsoft.com/en-us/library/bb384062.aspx [Object and Collection Initializers (C# Programming Guide)]

http://msdn.microsoft.com/en-us/library/bb384054.aspx [Auto-Implemented Properties (C# Programming Guide)]

 

PowerShell, The Beginning, .NET Integration

Today we are starting to talk about Windows PowerShell. There are going to series of posts about this wonderful scripting language. I am going to give a small code example and will be trying to explain how the code works.

In this example we going to look for the "logfiles" folder in the main drive where operating system is installed. If this folder does not exist we throw nice error message to the user otherwise we open this folder with explorer.

Here is how the codes looks like:

$disks = Get-PSProvider -PSProvider FileSystem | Select-Object Drives
$mainDrive = $disks.Drives[0].toString()

$script:ErrorActionPreference = "SilentlyContinue"

If (!(Test-Path -Path ("{0}:\logfiles" -f $mainDrive) -PathType "container"))
{
    [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
    [System.Windows.Forms.MessageBox]::Show("I am sorry, but the logs folder does not exist. Maybe you did not run the installation?",
    "Organization Projects Accounting System - Installation Wizard", [Windows.Forms.MessageBoxButtons]::OK, [Windows.Forms.MessageBoxIcon]::Warning)
}
Else
{
    explorer ("{0}:\logfiles" -f $mainDrive)
}

Let's look how the code works.

$disks = Get-PSProvider -PSProvider FileSystem | Select-Object Drives
$mainDrive = $disks.Drives[0].toString()

First of all let's talk about the variables. As you can see "$" marks named variable. $myVariable. Actually there are three types variables:

  1. User variables - this is variables that you define and use in PowerShell. They exist only in the current session of PowerShell this means after restarting PowerShell they are gone.
  2. Automatic variables - this more special variable, they are all create by the PowerShell itself and you can not make a change to them. Examples: $PSHome, $Host, $Args, $$, etc.
  3. Preference variables - this similar to automatic variables, the only difference that you can change values of them. After starting PowerShell session they have default values, changing them allows you control how the code should work in the current session. For eample we can say that on error PowerShell script should continue to execute with no error message going to user.
We are using first cmdlet, Get-PSProvider. It allows us to get all the operating system storage providers:

In this particular example we are getting FileSystem, because we want to find the main drive of operating system and this is the way to get drives list. So here we get a new object which has Drives property. We are using Select-Object cmdlet to get this property form the ProviderInfo object. In the second line we access the collection of the drives in the system. The property we took (Drives) with Select-Object has so called NoteProperty (property with static value), Drives (same name), with actually is collection object. We take the first drive from the collection and we make sure it is a string. Note: I can not guaranty that in this way we always will get main system drive, but for now it works as we want.

$script:ErrorActionPreference = "SilentlyContinue"

This next line tells that in this script all cmdlets should continue if error occurred and should not give any error message to the end user. There are many scopes supported in PowerShell with wide possibilities and we are not going to get into that just right now. ErrorActionPreference as you can is preference type variable, the default value of this is "Continue". When the error occurs it is displayed and script continues to execute. It is possible to set for cmdlet what to do on error by defying common parameter called -ActionError.

If (!(Test-Path -Path ("{0}:\logfiles" -f $mainDrive) -PathType "container"))
{
    [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
    [System.Windows.Forms.MessageBox]::Show("I am sorry, but the logs folder does not exist. Maybe you did not run the installation?",
    "Organization Projects Accounting System - Installation Wizard", [Windows.Forms.MessageBoxButtons]::OK, [Windows.Forms.MessageBoxIcon]::Warning)
}
Else
{
    explorer ("{0}:\logfiles" -f $mainDrive)
}

The last part of the code does the checking and displaying message of opening the folder. Here we use if statement and Test-Path cmdlet to check if the folder does exists. The first -Path parameter is a formatted string. Here we are using special operator "-f" which in the "{0}" position in the string will but the $mainDrive variable's value. -PathType parameter defines that we are looking for folder.

On the success we are using .NET Classes to show the error message for the user. "[]" brackets are used for this purpose. Inside Brackets we write full assembly name. "::" is used for method calling in .NET Classes. As you can see it is exactly the same as would be in the C# using .NET Framework the only difference is the syntax. Message:

On the failure we just open explorer with specified path to the "logfiles" folder.

This was very brief view of PowerShell capabilities. PowerShell is so wide and dynamic scripting language and it is very hard to cover every possible conner of it. Next time I am going to be more specific with more small examples and we are going to talk more about the features.

About Me

I am studying Software Engineering (3rd year) in Vilnius University, Lithuania. I am one of those guys interested in most of new technologies and trying to figure out the future.

WikiAnswers.com

If you are interested in asking me a question. I suggest posting this question in WikiAnswers.com proper category and informing me on WikiAnswers.com about the question.