Quick C#

Environment: .NET, C#, Win XP, Win 2000

Introduction

C# is a language with the features of C++, programming style like Java, and the rapid application model of Basic. If you already know the C++ language, it will take you less than an hour to quickly go through the syntax of C#. Familiarity with Java will be a plus because the Java program structure, the concept of packages, and garbage collection will definitely help you learn C# more quickly. So while discussing C# language constructs, I will assume that you know C++.

This article dicusses the C# language constructs and features, using code examples in a brief and comrehensive way so that you can, just by having a glance at the code, understand the concepts.

Note: This article is not for C# gurus. There must be some other beginner's articles on C#, but this is yet another one.

The following topics of the C# langauge are discussed:

  • Program Structure
  • Namespaces
  • Data Types
  • Variables
  • Operators and Expressions
  • Enumerations
  • Statements
  • Classes and Structs
  • Modifiers
  • Properties
  • Interfaces
  • Function Parameters
  • Arrays
  • Indexers
  • Boxing and Unboxing
  • Delegates
  • Inheritance and Polymorphism

The following topics are not discussed:

  • Things that are common in C++ and C#
  • Concepts such as garbage collection, threading, file processing, and so forth
  • Data type conversions
  • Exception handling
  • .NET library

Program Structure

Like C++, C# is case-sensitive. The semicolon, ;, is the statement separator. Unlike C++, there are no separate declaration (header) and implementation(cpp) files in C#. All code (class declaration and implementation) is placed in one file with a cs extention.

Have a look at this Hello world program in C#.

using System;

namespace MyNameSpace

{

class HelloWorld

{
    static void Main(string[] args)
    {
        Console.WriteLine ("Hello World");
     }
}

}

Everything in C# is packed into a class and classes in C# are packed into namespaces (just like files in a folder). Like C++, a main method is the entry point of your program. C++'s main function is called "main", whereas C#'s main function starts with a capital M and is named "Main".

There is no need to put a semicolon after a class block or struct definition. It was required in C++, but not in C#.

Namespace

Every class is packaged into a namespace. Namespaces are exactly the same concept as in C++, but in C# we use namespaces more frequently than in C++. You can access a class in a namespace using dot . qualifier. MyNameSpace is the namespace in the hello world program above.

Now, consider that you want to access the HelloWorld class from some other class in some other namespace.

using System;
namespace AnotherNameSpace
{
    class AnotherClass
    {
        public void Func()
        {
            Console.WriteLine ("Hello World");
        }
    }
}

Now, from your HelloWorld class, you can access it as:

using System;
using AnotherNameSpace;    // you will add this using statement
namespace MyNameSpace
{
class HelloWorld
{
    static void Main(string[] args)
    {
        AnotherClass obj = new AnotherClass();
        obj.Func();
    }
}
}

In the .NET library, System is the top-level namespace in which other namespaces exist. By default, there exists a global namespace, so a class defined outside a namespace goes directly into this global namespace; hence, you can access this class without any qualifier.

you can also define nested namespaces.

Using

The #include directive is replaced with the "using" keyword, which is followed by a namespace name. Just as in "using System", above. "System" is the base-level namespace in which all other namespaces and classes are packed. The base class for all object is Object in the System namespace.

Variables

Variables in C# are almost the same as in C++, except for these differences:

  1. Variables in C# (unlike C++) always need to be initialized before you access them; otherwise, you will get a compile time error. Hence, it's impossible to access an uninitialized variable.
  2. You can't access a .dangling. pointer in C#.
  3. An expression that indexes an array beyond its bounds is also not accessible.
  4. There are no global variables or functions in C# and the behavior of globals is acheived through static functions and static variables.

Data Types

All types of C# are derived from a base-class object. There are two types of data types:

  1. Basic/Built-in types
  2. User-defined types

Following is a table that lists built-in C# types:

Type

Bytes

Desc

byte 1 unsigned byte
sbyte 1 signed byte
short 2 signed short
ushort 2 unsigned short
int 4 signed integer
uint 4 unsigned integer
long 8 signed long
ulong 8 unsigned long
float 4 floating point number
double 8 double precision number
decimal 8 fixed precision number
string unicode string
char unicode char
bool true, false boolean
Note: Type range in C# and C++ are different; for example, long in C++ is 4 bytes, and in C# it is 8 bytes. Also, the bool type and string types are different than those in C++. Bool accepts only true and false and not any integer.

User-defined types include:

  1. Classes
  2. Structs
  3. Interfaces

Memory allocation of the data types divides them into two types:

  1. Value Types
  2. Reference Types

Value types

Value types are those data types that are allocated in the stack. They include:

  • All basic or built-in types except strings
  • Structs
  • Enum types

Reference type

Reference types are allocated on the heap and are garbage collected when they are no longer being used. They are created using the new operator, and there is no delete operator for these types, unlike in C++, where the user has to explicitely delete the types created using the delete operator. In C#, they are automatically collected by the garbage collector.

Reference types include:

  • Classes
  • Interfaces
  • Collection types such as arrays
  • String

Enumeration

Enumerations in C# are exactly like C++. They are defined through a enum keyword.

Example:

enum Weekdays
{
  Saturday, Sunday, Monday, Tuesday, Wednesday, Thursday, Friday
}

Classes and Structs

Classes and structs are same as in C++, except in the difference of their memory allocation. Objects of classes are allocated in the heap, and are created using new, where as structs are allocated in the stack. Structs in C# are very light and fast datatypes. For heavy datatypes, you should create classes.

Examples:

struct Date
{
    int day;
    int month;
    int year;
}

class Date
{
    int day;
    int month;
    int year;
    string weekday;
    string monthName;
    public int GetDay()
    {
        return day;
    }
    public int GetMonth()
    {
        return month;
    }
    public int GetYear()
    {
        return year;
    }
    public void SetDay(int Day)
    {
        day = Day ;
    }
    public void SetMonth(int Month)
    {
        month = Month;
    }
    public void SetYear(int Year)
    {
        year = Year;
    }
    public bool IsLeapYear()
    {
        return (year/4 == 0);
    }
    public void SetDate (int day, int month, int year)
    {
    }
    ...
}

Properties

If you familiar with the object-oriented way of C++, you must have an idea of properties. Properties in the above example of the Date class are day, month, and year for which in C++, you write Get and Set methods. C# provides a more convinient, simple, and straightforward way of accessing properties.

So, the above class can be written as:

using System;
class Date
{
    public int Day{
        get {
            return day;
        }
        set {
            day = value;
        }
    }
    int day;

    public int Month{
        get {
            return month;
        }
        set {
            month = value;
        }
    }
    int month;

    public int Year{
        get {
            return year;
        }
        set {
            year = value;
        }
    }
    int year;

    public bool IsLeapYear(int year)
    {
        return year%4== 0 ? true: false;
    }
    public void SetDate (int day, int month, int year)
    {
        this.day   = day;
        this.month = month;
        this.year  = year;
    }
}

Here is the way you will get and set these properties:

class User
{
   public static void Main()
   {
        Date date = new Date();
        date.Day = 27;
        date.Month = 6;
        date.Year = 2003;
        Console.WriteLine("Date: {0}/{1}/{2}", date.Day,
                                               date.Month,
                                               date.Year);
    }
}

Modifiers

You must be aware of public, private, and protected modifers that are commonly used in C++. Here, I will discuss some new modifiers introduced by C#.

readonly

The readonly modifier is used only for the class data members. As the name indicates, the readonly data members can only be read once they are written either directly initializing them or assigning values to them in constructor. The difference between the readonly and const data members is that const requires you to initialize with the declaration, that is directly. See this example code:

class MyClass
{
    const int constInt = 100;    //directly
    readonly int myInt = 5;      //directly
    readonly int myInt2;
    
    public MyClass()
    {
        myInt2 = 8;              //Indirectly
    }
    public Func()
    {
        myInt = 7;               //Illegal
        Console.WriteLine(myInt2.ToString());
    }
    
}

sealed

the sealed modifier with a class doesn't let you derive any class from it. So, you use this sealed keyword for the classes that you don't want to be inherited from.

sealed class CanNotbeTheParent
{
    int a = 5;
}

unsafe

You can define an unsafe context in C# by using an unsafe modifier. In the unsafe context, you can write an unsafe code; for example, C++ pointers and so forth. See the following code:

public unsafe MyFunction( int * pInt, double* pDouble)
{
    int* pAnotherInt = new int;
    *pAnotherInt  = 10;
    pInt = pAnotherInt;
    ...
    *pDouble = 8.9;
}

Interfaces

If you have an idea of the COM, you will immediately know what I am talking about. An interface is the abstract base class containing only the function signatures whose implementation is provided by the child class. In C#, you define such classes as interfaces using the interface keyword. .NET is based on such interfaces. In C#, where you can't use multiple class inheritance, which was previously allowed in C++, the essence of multiple inheritance is acheived through interfaces. That's how your child class may implement multiple interfaces.

using System;
interface myDrawing
{
    int originx
    {
        get;
        set;
    }
    int originy
    {
        get;
        set;
    }
    void Draw(object shape);
}

class Shape: myDrawing
{
    int OriX;
    int OriY;

    public int originx
    {
        get{
            return OriX;
        }
        set{
            OriX = value;
        }
    }
    public int originy
    {
        get{
            return OriY;
        }
        set{
            OriY = value;
        }
    }
    public void Draw(object shape)
    {
        ...    // do something
    }
    
    // class's own method
    public void MoveShape(int newX, int newY)
    {
    .....
    }
    
}

Arrays

Arrays in C# are much better than in C++. Arrays are allocated in the heap and thus are of the reference type. You can't access an out-of-bound element in an array. So, C# prevents you from that type of bug. Also, some helper functions to iterate array elements are provided. foreach is the statement for such an iteration. The difference between the syntax of the C++ and C# array is:

  • The square brackets are placed after the type and not after the variable name.
  • You create element locations using new operator.

C# supports single dimensional, multi dimensional, and jagged array (array of an array).

Examples:

    int[] array = new int[10];              // single-dimensional
                                            // array of int
    for (int i = 0; i < array.Length; i++)
        array[i] = i; 

    int[,] array2 = new int[5,10];          // 2-dimensional array
                                            // of int
    array2[1,2] = 5;

    int[,,] array3 = new int[5,10,5];       // 3-dimensional array
                                            // of int
    array3[0,2,4] = 9;

    int[][] arrayOfarray = = new int[2];    // Jagged array -
                                            // array of array of
                                            // int
    arrayOfarray[0] = new int[4]; 
    arrayOfarray[0] = new int[] {1,2,15};

Indexers

An indexer is used to write a method to access an element from a collection using the straight way of using [], like an array. All you need is to specify the index to access an instance or element. The syntax of an indexer is same as that of class properties, except they take the input parameter, that is the index of the element.

Example:

Note: CollectionBase is the library class used for making collections. List is the protected member of CollectionBase, which stores the collection list.
class Shapes: CollectionBase
{
    public void add(Shape shp)
    {
        List.Add(shp);
    }

    //indexer
    public Shape this[int index]
    {
        get {
            return (Shape) List[index];
        }
        set {
            List[index] = value ;
         }
     }
}

Boxing/Unboxing

The idea of boxing is new in C#. As mentioned above, all data types, built-in or user defined, are derived from a base class object in the System namespace. So, the packing of basic or primitive types into an object is called boxing, whereas the reverse of this known as unboxing.

Example:

class Test
{
   static void Main()
   {
      int myInt = 12;
      object obj = myInt ;       // boxing
      int myInt2 = (int) obj;    // unboxing
   }
}

The example shows both boxing and unboxing. An int value can be converted to an object and back again to an int. When a variable of a value type needs to be converted to a reference type, an object box is allocated to hold the value, and the value is copied into the box. Unboxing is just the opposite. When an object box is cast back to its original value type, the value is copied out of the box and into the appropriate storage location.

Function Parameters

Parameters in C# are of three types:

  1. By-Value/In parameters
  2. By-Reference/In-Out parameters
  3. Out pParameters

If you have an idea of the COM interface and its parameters types, you will easily understand the C# parameter types.

By-Value/In parameters

The concept of value parameters is the same as in C++. The value of the passed value is copied into a location and is passed to the function.

Example:

SetDay(5);
...
void SetDay(int day)
{
    ....
}

By-Reference/In-Out Parameters

The reference parameters in C++ are passed either through pointers or a reference operator, &. In C#, reference parameters are less error prone. Reference parameters are also called In-Out parameters because you pass a reference address of the location, so you pass an input value and get an output value from that function.

You cannot pass an uninitialized reference parameter into a function. C# uses a keyword ref for the reference parameters. You also have to use keyword ref with an argument while passing it to a function-demanding reference parameter.

Example:

    int a= 5;
    FunctionA(ref a);        // use ref with argument or you will
                             // get a compiler error
    Console.WriteLine(a);    // prints 20

    void FunctionA(ref int Val)
    {
        int x= Val;
        Val = x* 4;
    }

Out parameter

The Out parameter is the parameter that only returns a value from the function. The input value is not required. C# uses the keyword out for the out parameters

Example:

    int Val;
    GetNodeValue(Val);

    bool GetNodeValue(out int Val)
    {
        Val = value;
        return true; 
    }

Variable number of parameters and arrays

Arrays in C# are passed through a keyword params. An array-type parameter should always be the right-most argument of the function. Only one parameter can be of the array type. You can pass any number of elements as an argument of type of that array. You can better understand it from the following example.

Example:

    void Func(params int[] array)
    {
        Console.WriteLine("number of elements {0}",
                           array.Length);
    }

    Func();                      // prints 0
    Func(5);                     // prints 1
    Func(7,9);                   // prints 2
    Func(new int[] {3,8,10});    // prints 3
    int[] array = new int[8] {1,3,4,5,5,6,7,5};
    Func(array);                 // prints 8

Operators and Expressions

Operators are exactly the same as om C++ and thus the expression, also. However, some new and useful operators have been added. Some of them are discussed here.

is operator

The is operator is used to check whether the operand types are equal or convertable. The is operator is particularly useful in the polymorphism scenarios. The is operator takes two operands and the result is a boolean. See the example:

void function(object param)
{ 
    if(param is ClassA)
        //do something
    else if(param is MyStruct)
        //do something
    }
}

as operator

The as operator checks whether the type of the operands are convertable or equal (as is done by is operator) and if it is, the result is a converted or boxed object (if the operand can be boxed into the target type, see boxing/unboxing). If the objects are not convertable or boxable, the return is a null. Have a look at the example below to better understand the concept.

Shape shp = new Shape();
Vehicle veh = shp as Vehicle;   // result is null, types are not
                                // convertable

Circle cir = new Circle();
Shape shp = cir;
Circle cir2 = shp as Circle;    //will be converted

object[] objects = new object[2];
objects[0] = "Aisha";
object[1] = new Shape();

string str;
for(int i=0; i&< objects.Length; i++)
{
    str = objects[i] as string;
    if(str == null)
        Console.WriteLine("can not be converted");
    else
        Console.WriteLine("{0}",str);
}

Output:
Aisha
can not be converted

Statements

Statements in C# are just like in C++ except some additions of new statements and modifications in some statements.

The following are new statements:

foreach

For iteration of collections, such as arrays, and so forth.

Example:

    foreach (string s in array)
        Console.WriteLine(s);

lock

Used in threads for locking a block of code, making it a critical section.

checked/unchecked

The statements are for overflow checking in numeric operations.

Example:

int x = Int32.MaxValue; x++;    // Overflow checked
{ 
x++;                            // Exception
}
unchecked
{
x++;                            // Overflow}
}

The following statements are modified:

Switch

The Switch statement is modified in C#.

  1. Now, after executing a case statement, program flow cannot jump to the next case, which was previously allowed in C++.
    Example:
    int var = 100;
    switch (var)
    {
        case 100: Console.WriteLine("<Value is 100>");
            // No break here
        case 200: Console.WriteLine("<Value is 200>"); break;
    }
    Output in C++:
        <Value is 100><Value is 200>
    

    In C#, you get a compile time error:

    error CS0163: Control cannot fall through from one case label
    ('case 100:') to another
    
  2. However, you can do this similarly to the way you do it in C++:
    switch (var)
    {
        case 100:
        case 200: Console.WriteLine("100 or 200<VALUE is 200>");
                  break;
    }
    
  3. You also can use constant variables for case values:
  4. Example:

    const string WeekEnd  = "Sunday";
    const string WeekDay1 = "Monday";
    
    ....
    
    string WeekDay = Console.ReadLine();
    switch (WeekDay )
    {
    case WeekEnd: Console.WriteLine("It's weekend!!"); break;
    case WeekDay1: Console.WriteLine("It's Monday"); break;
    
    }
    

Delegates

Delegates let us store function references into a variable. In C++, this is like using and storing a function pointer for which we usually use typedef.

Delegates are declared using a keyword delegate. Have a look at this example, and you will understand what delegates are:

Example:

delegate int Operation(int val1, int val2);
public int Add(int val1, int val2)
{
    return val1 + val2;
}
public int Subtract (int val1, int val2)
{
    return val1- val2;
}

public void Perform()
{
    Operation Oper;
    Console.WriteLine("Enter + or - ");
    string optor = Console.ReadLine();
    Console.WriteLine("Enter 2 operands");

    string opnd1 = Console.ReadLine();
    string opnd2 = Console.ReadLine();

    int val1 = Convert.ToInt32 (opnd1);
    int val2 = Convert.ToInt32 (opnd2);

    if (optor == "+")
        Oper = new Operation(Add);
    else
        Oper = new Operation(Subtract);
        
    Console.WriteLine(" Result = {0}", Oper(val1, val2));
}

Inheritance and Polymorphism

Only single inheritance is allowed in C#. Mutiple inheritance can be acheived by using interfaces.

Example:

class Parent{
}

class Child : Parent

Virtual Functions

Virtual functions implement the concept of polymorphism are the same as in C#, except that you use the override keyword with the virtual function implementaion in the child class. The parent class uses the same virtual keyword. Every class that overrides the virtual method will use the override keyword.

class Shape
{
    public virtual void Draw()
    {
        Console.WriteLine("Shape.Draw")    ;
    }
}

class Rectangle : Shape

{
    public override void Draw()
    {
        Console.WriteLine("Rectangle.Draw");
    }
}

class Square : Rectangle
{
    public override void Draw()
    {
        Console.WriteLine("Square.Draw");
    }
}
class MainClass
{
    static void Main(string[] args)
    {
        Shape[] shp = new Shape[3];
        Rectangle rect = new Rectangle();
        
        shp[0] = new Shape();
        shp[1] = rect;
        shp[2] = new Square();

        shp[0].Draw();
        shp[1].Draw();
        shp[2].Draw();
    }
}
Output:
Shape.Draw
Rectangle.Draw
Square.Draw

Hiding parent functions using "new"

You can define in a child class a new version of a function, hiding the one which is in base class. A new keyword is used to define a new version. Consider the example below, which is a modified version of the above example and note the output this time, when I replace the override keyword with a new keyword in the Rectangle class.

class Shape
{
    public virtual void Draw()
    {
        Console.WriteLine("Shape.Draw")    ;
    }
}

class Rectangle : Shape
{
    public new void Draw()
    {
        Console.WriteLine("Rectangle.Draw");
    }
}
class Square : Rectangle
{
    //wouldn't let you override it here
    public new void Draw()
    {
        Console.WriteLine("Square.Draw");
    }
}
class MainClass
{
    static void Main(string[] args)
    {
        Console.WriteLine("Using Polymorphism:");
        Shape[] shp = new Shape[3];
        Rectangle rect = new Rectangle();

        shp[0] = new Shape();
        shp[1] = rect;
        shp[2] = new Square();

        shp[0].Draw();
        shp[1].Draw();
        shp[2].Draw();

        Console.WriteLine("Using without Polymorphism:");
        rect.Draw();
        Square sqr = new Square();
        sqr.Draw();
    }
}

Output:
Using Polymorphism
Shape.Draw
Shape.Draw
Shape.Draw
Using without Polymorphism:
Rectangle.Draw
Square.Draw

The polymorphism doesn't take the Rectangle class's Draw method as a polymorphic form of the Shape's Draw method. Instead, it considers it a different method. So, to avoid the naming conflict between parent and child, we have used the new modifier.

Note: You cannot use the two version of a method in the same class, one with new modifier and other with override or virtual. As in the above example, I cannot add another method named Draw in the Rectangle class which is a virtual or override method. Also, in the Square class, I can't override the virtual Draw method of the Shape class.

Calling base class members

If the child class has the data members with same name as that of base class, to avoid naming conflicts, base class data members and functions are accessed using a keyword base. See in the examples how the base class constructors are called and how the data members are used.

public Child(int val) :base(val)
{
    myVar = 5;
    base.myVar;
}

OR

public Child(int val)
{
    base(val);
    myVar = 5 ;
    base.myVar;
}

Future Additions:

This article is just a quick overwiew of the C# language so that you can just become familiar with the langauge features. Although I have tried to discuss almost all the major concepts in C# in a brief and comprehensive way with code examples, I think there is lot much to be added and discussed.

In the future, I would like to add more commands and concepts not yet discussed including events and so forth. I would also like to write about Windows programming using C# for beginners.

References

  • our most commonly known MSDN
  • Inside C# by Tom Archer
  • A Programmer's Introduction to C# by Eric Gunnerson
  • Beginning C# by Karli Watson
  • Programming C# (O'Reilly)

About the Author

Aisha is a Master of Science in Computer Science from Quaid-i-Azam Univeristy. She has worked in VC++ 6, MFC, ATL, COM/DCOM, ActiveX, C++, SQL, and so forth. These days she is working on .NET framework and C#. Inspired with nature, she loves to seek knowledge. She is also fond of travelling. She keeps a free source code and articles Web site at http://aishai.netfirms.com.



Comments

  • Good

    Posted by Legacy on 02/24/2004 12:00am

    Originally posted by: Humayun Faiz Abbas

    Dear Aisha,
    thank u very much for your efforts

    Reply
  • Too complicated for ME... gEEkSpEEK is sooooo boring.

    Posted by Legacy on 02/17/2004 12:00am

    Originally posted by: CodeGURL

    there is nothing to read here, WHY ARE YOU LOOKING???

    Reply
  • Great Work. ... Needs your contributions to the topics which are not discussed in this article

    Posted by Legacy on 11/27/2003 12:00am

    Originally posted by: vedhamanikandan

    Examples itself is so explanatory that more theory is not necessary. Please continue your work in the future to develop all the young developers in future.
    

    Reply
  • Great examples.......Needs another version to describe rest of the topics

    Posted by Legacy on 11/04/2003 12:00am

    Originally posted by: Rupa

    C# language is written in a very brief and specific manner which will be very helpful for any new developer to understand in a day or two. Examples itself is so explanatory that more theory is not necessary.

    Reply
  • Good quick tutorial

    Posted by Legacy on 10/28/2003 12:00am

    Originally posted by: Łukasz Ślachciak

    This is very good turorial for fast acccomodation to C#. Especially I would like to recommend it for C++ programmers.

    Reply
  • Very Good!

    Posted by Legacy on 10/13/2003 12:00am

    Originally posted by: Phanikumar

    The article gives a very good overview of C# for C++ programmers. Thanks to Aisha Ikram!!

    --
    Phanikumar

    Reply
  • Great!

    Posted by Legacy on 09/09/2003 12:00am

    Originally posted by: Anna

    This is a great web side

    Reply
  • Very Good!!! And the second part... When?

    Posted by Legacy on 07/07/2003 12:00am

    Originally posted by: Jorge Rojas

    Congratulations! Very good and clear. It's really a "quick" start, and you've done a good work not messing with provided classes and extras, just the language.

    I cannot wait for the second part!!!

    Keep the good work.

    Reply
  • Programming Alam to the New C# Programmers

    Posted by Legacy on 07/03/2003 12:00am

    Originally posted by: Malik Asif Joyia

    A Very Nice work by you
    carry on and Keep it up
    Thanks

    Reply
  • Simply Superb

    Posted by Legacy on 07/01/2003 12:00am

    Originally posted by: Jitendra

    This article is simply superb.
    I liked it very much.

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Event Date: April 15, 2014 The ability to effectively set sales goals, assign quotas and territories, bring new people on board and quickly make adjustments to the sales force is often crucial to success--and to the field experience! But for sales operations leaders, managing the administrative processes, systems, data and various departments to get it all right can often be difficult, inefficient and manually intensive. Register for this webinar and learn how you can: Align sales goals, quotas and …

  • Live Event Date: August 14, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT Data protection has long been considered "overhead" by many organizations in the past, many chalking it up to an insurance policy or an extended warranty you may never use. The realities of today makes data protection a must-have, as we live in a data-driven society -- the digital assets we create, share, and collaborate with others on must be managed and protected for many purposes. Check out this upcoming eSeminar and join Seagate Cloud …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds