Chapter 3 - Core C# Programming Constructs Part 2

The content and code of this article is referenced from book Pro C#5.0 and the .NET 4.5 Framework by Apress. The intention of the writing is to review the konwledge and gain better understanding of the .net framework. 

 

1. Methods and parameters modifiers

Methods can be implemented within the scope of classes or structures (as well as interface type) and may decorated with various keywords (e.g static, virtual, public, new) to qualify their behavior. 

C# parameter modifiers

(None)                          If a parameter is not marked with a parameter modifier, it is assumed to be passed by value, meaning the called method receives a copy of the orginal data.
out Output parameter must be assigned by the method being called, and therefore, are passed by reference. If the called method failed to assign output parameters, you are issued a compiler error. 
ref The value is initially assigned by the caller and may be optionally reassigned by the called methods. No compiler error is generated if the called method fails to assign a ref parameter. 
params The parameter modifier allows you to send in a variable number of arguments as a single logical parameter. A method can only have a single params modifier, and it must be the final parameter of the method.

1.1 the default by value parameter-passing behavior

1.2 The out modifier


Methods that have been defined to take output parameters (via the keyword out) are under obligation to assign them to an appropriate value before exiting the method scope. 

1 public static void Add(int x, int y, out int answer)
2 {
3     answer = x + y; //answer must be assigned value, as it is out parameter
4 }

 

The advantage of using out parameters is it allows the caller to obtain multiple ouputs from a single method invocation. 

1 public static void FillTheseValues(out int a, out string b, out bool c)
2 {
3     a = 9;
4     b = "test";
5     c = true;
6 }

 

1.3 The ref modifier

Reference parameters are necessary when you wish to allow a method to operate on (and usually change the values of ) various data points declared in the caller's scope. And reference type must be initialized before they are passed to the method. 

 1         public static void Main (string[] args)
 2         {    
 3             string str1 = "Flip";
 4             string str2 = "Flop";
 5             SwapString (ref str1, ref str2);
 6             Console.WriteLine ("After :{0}, {1}", str1, str2);
 7         }
 8 
 9         public static void SwapString(ref string s1, ref string s2)
10         {
11             string temStr = s1;
12             s1 = s2;
13             s2 = temStr;
14         }

 

1.4 The params modifier

The params keyword allows you to pass into a method a variable number of identically typed parameters as a single logical parameters. As well, arguments marked with the params keyword can be processed if the caller sends in a strongly typed array or a comma-delimited list of items. 

 

 1         public static void Main (string[] args)
 2         {    
 3             //commo delimited number
 4             double avg = CalculateAverage (4.0, 3.5, 2.8, 10.0);
 5             //pass an array of doubles
 6             double[] data = {4.5, 6.7, 8.9};
 7             avg = CalculateAverage (data);
 8         }
 9 
10         //the method has been defined to take a parameter array of doubles
11         public static double CalculateAverage(params double[] values)
12         {
13             double sum = 0;
14             if (values.Length == 0)
15                 return sum;
16 
17             for (int i = 0; i < values.Length; i++) {
18                 sum += values [i];
19             }
20             return (sum / values.Length);
21         }

 

The technique is nothing more than a convenience for the caller, given that the array is created by the CLR as necessary. 

 

1.5 optional parameters

C# allows you to create methods that can take optional arguments. The technique allows the caller to invoke a single method while omitting arguments deemed unnecessary, provided the caller is happy with the specified defaults. 

 

 1         public static void Main (string[] args)
 2         {    
 3             EnterLogData ("error occurs"); //invoke method without owner parameter
 4             EnterLogData ("error occurs", "manager"); //invoke method with both parameters
 5         }
 6 
 7         //parameter owner has a default value of programmer
 8         static void EnterLogData(string message, string owner = "programmer")
 9         {
10             Console.WriteLine ("{0}, {1}", message, owner);
11         }

 

One important thing to be aware of, is that the values assigned to an optional parameter must be known at compile time, and can not be resolved at runtime. 

1         //parameter owner has a default value of programmer
2         static void EnterLogData(string message, string owner = "programmer", DateTime timeStamp = DateTime.Now)  //compile error, datetime value is resolved at runtime
3         {
4             Console.WriteLine ("{0}, {1}", message, owner);
5         }

 

1.6 Invoking methods using named parameters

Another language feature found in C# is support for named arguments. Named arguments allow you to invoke a method by specifying parameter values in any order you choose. Thus, rather than passing parameters solely by position, you can choose to specify each argument by name using a colon operator. 

 1         public static void Main (string[] args)
 2         {    
 3             EnterLogData (message:"error occurs", owner:"programmer"); //invoke method with name parameters
 4 
 5         }
 6             
 7         static void EnterLogData(string message, string owner)
 8         {
 9             Console.WriteLine ("{0}, {1}", message, owner);
10         }

 

1.7 Method overloading

Method overloading, when you define a set of identically named methods that differ by the number (or type) or parameters, the method in question is said to be overloaded. 

 1         public static void Main (string[] args)
 2         {    
 3             int answer = Add (4, 5);
 4             double answerD = Add (4.0, 5.5);
 5         }
 6             
 7         static int Add(int x, int y)
 8         {
 9             return x + y;
10         }
11 
12         static double Add(double x, double y)
13         {
14             return x + y;
15         }

 

2. Arrays 


An Array is a set of data items, accessed using a numerical index. 

 

static void SimpleArray()
{
    int[] myInts = new int[3]; //index starts from 0
    myInts [0] = 100;
    myInts [1] = 200;
    myInts [2] = 300;

    foreach (int i in myInts) {
        Console.WriteLine (i);
    }
}

2.1 Array initialization syntax

You are able to fill the items of an array using C# array initialization syntax. 

 

static void SimpleArray()
{
    int[] myInts = { 10, 20, 30 }; //array initialization with curly brackets

    foreach (int i in myInts) {
    Console.WriteLine (i);
}

 

Notice that when you make use of this syntax, you do not need to specify the size of the array, given that this will be inferred by the number of items within the scope of the curly brackets. Also the use of new keyword is optional. 

 

2.2 Implicitly typed local arrays


The var keyword can be used to define implicity typed local arrays. 

 

1         static void SimpleArray()
2         {
3             var a = new int[] {10, 20, 30}; // int array
4                     
5             foreach (var i in a) {
6                 Console.WriteLine (i);
7             }
8 
9         }

 

2.3 Defining an array of objects

In most cases, when you define an array, you do so by specifying the explicit type of item that can be within the array variable. As the System.Object is the ultimate base class to each and every type in the .net system, if you define the array of objects, the subitems could be anything at all. 

 

 1         static void ArrayOfObjects()
 2         {
 3             object[] myObjects = new object[4];
 4 
 5             myObjects [0] = 10;
 6             myObjects [1] = true;
 7             myObjects [2] = "test";
 8             myObjects [3] = 23.3;
 9 
10             foreach (object obj in myObjects) {
11                 Console.WriteLine ("Type : {0}, Value : {1}", obj.GetType (), obj);
12             }
13         }

 

2.4 Multidimensional arrays

In addition to the single-dimension array you have seen so far, C# also supports two varieties of multidimensional arrays. The first one is termed a rectangular array, which is simple an array of multiple dimensions, where each row is of the same length. 

 

 1         static void RectMultidimensionalArray()
 2         {
 3             int[,] myMatrix = new int[6, 6];
 4 
 5             for (int i = 0; i < 6; i++) {
 6                 for (int j = 0; j < 6; j++) {
 7                     myMatrix [i, j] = i * j;
 8                 }
 9             }
10 
11             //print value 
12             for (int i = 0; i < 6; i++) {
13                 for (int j = 0; j < 6; j++) {
14                     Console.Write (myMatrix [i, j] + "\t");
15                 }
16                 Console.WriteLine ();
17             }
18 
19         }

 

The second type of multidimensional array is termed a jagged array, which contains some number of inner arrays, each of which may have a different upper limit. 

 1         static void JaggedMultidimensionalArray()
 2         {
 3             int[][] myJagArray = new int[5][];
 4 
 5             for (int i = 0; i < myJagArray.Length; i++) {
 6                 myJagArray [i] = new int[i + 7];
 7             }
 8 
 9             for (int i = 0; i < 5; i++) {
10                 for (int j = 0; j < myJagArray [i].Length; j++) {
11                     Console.Write (myJagArray [i] [j] + " ");
12                 }
13                 Console.WriteLine ();
14             }
15         }

 

2.5 Arrays as arguments or return values

You are free to pass array as an argument or receive it as a member return value. 

 

1         static void PrintArray(int[] myInts)
2         {
3             foreach (int i in myInts) {
4                 Console.WriteLine (i);
5             }
6         }        

 

2.6 System.Array base class


Each array you create gathers much of its functionality from the System.Array class. 

Clear() This static method sets a range of elements in the array to empty values
CopyTo() This method is used to copy elements from the source array into the destination array
Length This property returns the number of items within the array
Rank  
Reverse() This static method reverse the content of a one-dimensional array
Sort() This static method sorts a one-dimensional array of intrinsic type. 

 

 

 1         static void SystemArrayFunctionality()
 2         {
 3             string[] myString = { "text 1", "text 2", "text 3" };
 4             //reverse the string
 5             Array.Reverse (myString);
 6             foreach (string s in myString) {
 7                 Console.WriteLine (s);
 8             }
 9 
10             //clear the array
11             Array.Clear(myString, 0, 2);
12             foreach (string s in myString) {
13                 Console.WriteLine (s);
14             }
15         }    

 

3. Enum type

When building a system, it is often convenient to create a set of symbolic names that map to known numerical values. C# supports the notion of custom enumerations for this very reason.

 

        enum EmpType
        {
            Manager, //0
            Grunt, //1
            Contractor, //2
            VicePresiden //3
        }

 

The EmpType enumeration defines four named constants, corresponding to discrete numerical values. By default, the first element is set to value zero, followed by an n + 1 progression. You are also free to change the initial value. 

        enum EmpType
        {
            Manager = 102, //102
            Grunt, //103
            Contractor, //104
            VicePresiden //105
        }

Enumerations do not necessarily need to follow a sequential ordering. 

        enum EmpType
        {
            Manager = 102, 
            Grunt = 10, 
            Contractor = 8, 
            VicePresiden = 1 
        }

 

 

3.1 underlying storage for an enum

By default, the storage type used to hod the values of an enumeration is System.Int32; however, you are free to change this you liking. 

        enum EmpType : byte
        {
            Manager = 102, 
            Grunt = 10, 
            Contractor = 8, 
            VicePresiden = 1 
        }

 

3.2 declare enum

Enumerations are nothing more than a user-defined data type, you are able to use them as function return values, method parameters, local variables, and so forth. 

 

 1         public static void Main (string[] args)
 2         {    
 3             EmpType emp = EmpType.Contractor;
 4             AskForBonus (emp);
 5         }
 6             
 7         static void AskForBonus(EmpType e)
 8         {
 9             switch (e) 
10             {
11             case EmpType.Manager:
12                 Console.WriteLine ("manager bouns");
13                 break;
14             case EmpType.Grunt:
15                 Console.WriteLine ("grunt bouns");
16                 break;
17             case EmpType.Contractor:
18                 Console.WriteLine ("contractor bouns");
19                 break;
20             case EmpType.VicePresiden:
21                 Console.WriteLine ("vp bouns");
22                 break;
23             default:
24                 Console.WriteLine ("default bouns");
25                 break;
26             }
27         }

 

 

 

3.3 System.Enum type

The interesting thing about .net enumerations is that they gain functionality from the System.Enum class type. This class defines a number of methods that allow you to interrogate and transform a given enumeration. One helpful method is the static Enum.GetUnderlyingType()

 

        public static void Main (string[] args)
        {    
            EmpType emp = EmpType.Contractor;
            AskForBonus (emp);

            Console.WriteLine ("EmpType uses a {0} for storage", Enum.GetUnderlyingType (emp.GetType()));
            //or
            Console.WriteLine ("EmpType uses a {0} for storage", Enum.GetUnderlyingType(typeof(EmpType)));
        }

 

 

3.4 Dynamically discovering an enum's name/value pairs

Beyond the Enum.GetUnderlyingType() method all c# enumerations support a method named toString(), which returns the string name of the current enumeration's value. 

        public static void Main (string[] args)
        {    
            EmpType emp = EmpType.Contractor;
            AskForBonus (emp);

            Console.WriteLine ("emp is {0}", emp.ToString());
        }

If you are interested in discovering the value of a given enumeration variable, rather than its name, you can simply cast the enum variable against the underlying storeage type. 

        public static void Main (string[] args)
        {    
            EmpType emp = EmpType.Contractor;
            AskForBonus (emp);

            Console.WriteLine ("emp is {0}, value is {1}", emp.ToString(), (byte)emp);
        }

System.Enum also defines another static method named GetValues(). This method returns an instance of System.Array. Each item in the array corresponds to a member of the specified enumeration. 

        public static void Main (string[] args)
        {    
            Array enumData = Enum.GetValues (typeof(EmpType));
            for (int i = 0; i < enumData.Length; i++) {
                Console.WriteLine ("Name : {0}, Value: {0:D}", enumData.GetValue (i));
            }
        }

 

4. Struct type

 Struct is a user-defined type, however structures are not simply a collection of name/value paris. Rather, structures are types that can contain any number of data fields and members that operate on these fields. 

You can think of a structure as a "lightweight class type", given that structures provide a way to define a type that supports encapsulation, but cannot be build a family of related types. 

 1         struct Point
 2         {
 3             //fields of the structure
 4             public int x;
 5             public int y;
 6 
 7             public Point(int xPos, int yPos)
 8             {
 9                 x = xPos;
10                 y = yPos;
11             }
12 
13             //add 1 to the (x, y) position
14             public void Increment()
15             {
16                 x++;
17                 y++;
18             }
19 
20             //subtract 1 from the (x, y) position
21             public void Decrement()
22             {
23                 x--;
24                 y--;
25             }
26 
27             public void Display()
28             {
29                 Console.WriteLine ("x = {0}, y = {1}", x, y);
30             }
31         }
32             
33 
34         public static void Main (string[] args)
35         {    
36             Point p1 = new Point (10, 10);
37             p1.Display ();
38         }

 

5. Value type vs Reference type

Unlike array, string, or enumerations, c# structure does not have an identically named representation in the .net library, but are implicitly derived from System.ValueType. The role of System.ValueType is to ensure that the derived type is allocated on the stack, rather than the garbage-collected heap. The data in the stack can be created and desctoryed very quickly, as it lifetime is determined by the defining scope. Heap allocated data, on the other hand, is monitored by .net garbage collector. 

 

1         public abstract class ValueType : Object
2         {
3             public virtual bool Equals(object obj);
4             public virtual int GetHashCode();
5             public Type GetType();
6             public virtual string ToString();
7         }        

 

Functionally, the only purpose of System.ValueType is to override the virtual method defined by System.Object to use value-based, versus reference-based. 

When a value type variable falls out of the defining scope, it is removed from memory immediately.

        static void LocalValueTypes()
        {
            int i = 0;
            float f = 0.0f;
        } // i and f are removed from stack here

 

5. 1 value type, reference type and assignment operator

When you assign one value type to another, a member-by-member copy of the field is achieved. In constract to value types, when you apply the assignment operator to reference types, you are redirecting what the reference variable points to in memory. 

 

5.2 passing reference by value

 1     public class Person
 2     {
 3         public string personName;
 4         public int personAge;
 5 
 6         public Person (string name, int age)
 7         {
 8             personName = name;
 9             personAge = age;
10         }
11 
12         public void Display()
13         {
14             Console.WriteLine ("Name: {0}, Age: {1}", personName, personAge);
15         }
16     }
 1         public static void Main (string[] args)
 2         {    
 3             Person fred = new Person ("Fred", 12);
 4             fred.Display ();
 5 
 6             //call method 
 7             SendAPersonByValue(fred);
 8             fred.Display ();
 9         }
10 
11         static void SendAPersonByValue(Person p)
12         {
13             p.personAge = 99; //working
14 
15             p = new Person("test", 100); //not working
16         }  

The value of personAge has been changed to 99, which the name is still Fred. Therefore, while we pass the reference type by value, .net only copy the reference to the caller's object. As a result, we are able to change the state of the object, but we can't reassign what the reference is pointing to . 

5.3 Passing reference types by reference

 

1         static void SendAPersonByReference(ref Person p)
2         {
3             p.personAge = 99;
4 
5             p = new Person("test", 100);
6         }  

As you might expect, this allows complete flexibility of how the callee is able to manipulate the incoming parameter. Not only the callee change the state of the object, but if it so chooses, it may also reassign the reference to a new person object. 

5.4 Vaue type vs Reference type

Where are objects allocated? stack heap
How is a variable represented? value type are local copies reference type are pointing to the memory 
What is the base type? System.ValueType   any other type (except System.ValueType)
Can this type function as a base to other types? No, ValueTypes are always sealed and can not be inherited from yes, as long as it is not sealed class
What is the default parameter passing behavior passed by value   For value type, the object is copied-by_value. For reference type, the reference is copied by value
Can this type override system.Object.Finalize()? no yes
Can I define constructors for this type? yes     yes
When do variables of this type die? fall out of the defining scope   when the object is garbage collected
     

 

 

6. Nullable type


Value type can never be assigned the value of null, as that is used to establish an empty object reference. However, C# supports the concept of nullable data types. The nullable type can represent all the values of its underlying type, plus the value null. This is extremely helpful when working with relational database, given that it is quite common to encounter undefined columns in database tables. 

To define a nullable variable type, the question mark symbol is suffixed to the underlying data type. 

 

        static void LocalNullableValues()
        {
            int? nullableInt = 10;
            double? nullableDouble = null;
        }  

 

In C#, the ? suffix notation is shorthand for creating an instance of the generic System.Nullable<T> strcuture type. 

 

 

        static void LocalNullableValues()
        {
            Nullable<int> nullableInt = 10;
            Nullable<double> nullableDouble = null;
        }  

 

static void LocalNullableValues()
        {
            Nullable<int> nullableInt = 10;
            Nullable<double> nullableDouble = null;

            if (nullableInt.HasValue) {
                Console.WriteLine ("it is not null");
            }
        }  

6.1 ?? operator

This operator allows you to assign a value to a nullable type if the retrieved value is in fact null. 

int value = nullableInt ?? 11; //if it is null, assign 11 to value

 

posted @ 2015-03-16 21:59  tim_bo  阅读(176)  评论(0编辑  收藏  举报