Upload
vuongnhi
View
213
Download
0
Embed Size (px)
Citation preview
Emilee King C# CSC 415
History of C#
Development
In the late 1990’s Microsoft recognized the need to be able to develop applications that
can run on multiple operating system platforms; Microsoft’s solution to this was the .NET
Framework. In simple terms, .NET splits an operating system’s platform (be it Windows, Linux,
Mac OS, or any other OS) into two layers: a programming layer and an execution layer (Willis &
Newsome, 2008). In the early pre-release stages of the .NET Framework development the
framework used multiple languages such as C++, Visual, and ASP all of which were used for
different parts of the framework and were not interchangeable. Anders Hejlsberg and his team
wanted to be able to offer a more unified solution for this framework. Hejlsberg also notes that
along with unification “We also wanted to introduce modern concepts, such as object orientation,
type safety, and garbage collection and structured exception handling directly into the platform.
(Hamilton, 2008)”
Naming
During the initial development of C# the name for the language was Cool, which stood
for “C like Object Oriented Language.” However, due to the difficulty of trademarking such a
common word, Microsoft decided against keeping that name. Since the language was based off
of the programming language C, they still wanted a reference to C in the name. The language of
C++ was named in a similar fashion, as “++” is a way of incrementing a variable and C++ was
seen as an increment of C. The naming committee for C# decided add to that and use “a little
word play on C++, as you can sort of view the sharp sign as four pluses, so it’s C++++
1
Emilee King C# CSC 415
(Hamilton, 2008).” The name for C# was changed and the language was publicly announced
alongside the .NET project at the July 2000 Professional Developers Conference.
Names and Bindings
Names
C-based languages are case sensitive, so uppercase and lowercase letters matter while
naming identifiers. This means that two identifiers while they may be spelled the same will have
no connection between each other if there is a case difference. For example, the following
variables in C# have no connection to each other: sumOfAverages, sumofaverages,
SUMOFAVERAGES, and SumOfAverages. Due to the fact that it is impossible to please
everyone, especially when it comes to something that is being used by a large number of people,
there are arguments for and against case sensitivity. While other languages have developed
coding conventions that programmers of that language use to help deal with the issue of
sensitivity, for an example C programmers do not name their variables with an upper case letters,
C# was unable to implement such a standard due to the fact that many predefined names include
both uppercase and lowercase letters.
C# like all other languages has a set of specific naming conventions; naming conventions
are a set of do’s and don’ts that are considered best practices for a specific language. The
program will still run as intended if a programmer chooses not to follow these practices, and
many companies choose to tweak these practices to their own specific needs. Having standards
that are consistently used by all programmers of a certain language helps ensure that the code
will have better maintainability, readability, and writeability by ensuring that developers are
writing the same way so another developer can easily pick up the code later if needed. C#’s
2
Emilee King C# CSC 415
naming conventions include: PascalCasing for class names and method names, camelCasing for
method arguments and local variables, use predefined names instead of system type names, use
noun or noun phrases to name a class, prefix interfaces with the letter I, name source files
according to their main classes, use singular names for enums except for bit field enums, do not
suffix enums names with Enum, do not use abbreviations unless it is a commonly used name
such as html, avoid using SCREAMINGCAPS for constants or readonly variables, do not use
underscores in identifiers, unless it’s to prefix private static variables, and do not use type
indicators in identifier names.
C# uses keywords, which are predefined, reserved identifiers that have special meanings
to the compiler (Microsoft , n.d.). C# has two different types of keywords, contextual keywords
and reserved keywords. The difference between contextual keywords and reserved keywords is
that contextual keywords are only treated as keywords in certain situations while reserved
keywords are treated like keywords in all situations, however both are always lowercase. An
example of a contextual keyword is the keyword “value”, value can be used as an identifier in
most places except in property declarations and in event handlers. An example of the contextual
keyword value in a property declaration can be seen in the following code sample from
Microsoft Developer Network:
private int num;
public virtual int Number
{
get {return num; }
set { num = value; }
}
(Microsoft , n.d.)
3
Emilee King C# CSC 415
In the above code sample “value” is recognized as a keyword, however because value is a
contextual keyword the following declaration is also valid:
int value;
as long as it is not inside of a property declaration or an event handler. As mentioned earlier,
reserved words are recognized as keywords in all situations; so the following declaration is never
valid in C#:
int if;
because “if” is a reserved word that is used to identify whether a statement should execute based
on the value of a Boolean expression. However, unlike some languages, C# allows the reserved
keywords to be used as identifiers only if the programmer uses an “@” symbol as a prefix. So
while the previous declaration is invalid, the following declaration is valid:
int @if;
because of the @ prefix, the complier now recognizes that as a valid identifier. By allowing a
keyword to be interpreted by the compiler as an identifier “allows C# code written in other .NET
languages where an identifier might have the same name as a C# keyword (Deitel & Deitel,
2005).” While Microsoft tries to keep C# updated by adding new keywords as needed these new
keywords are always added as contextual keywords to avoid breaking pre-existing code.
Bindings
When it comes to bindings there are two main types: static and dynamic. Static binding is
when operations are executed during compile time and errors are detected and reported by the
compiler. While static binding happens at compile time, dynamic binding happens at runtime and
4
Emilee King C# CSC 415
the compiler does not check for errors like it does for static binding. Instead, errors with runtime
binding are now exceptions. While C# defaults to static binding, it also allows the choice to bind
certain operations dynamically such as: member access, method invocation, delegate invocation,
element access, object creation, overloaded binary operators, overloaded unary operators,
assignment operators, implicit conversions, and explicit conversions.
Data Types
C# is a strongly typed language, what that means is that variables cannot be used without a
data type being specified. C# has two different types of data types: value types and reference
types. It is important to understand the differences between the two types because of how each
type manages data and memory and how that will affect an application’s performance. All of the
basic, pre-defined, and user defined structures (structs) are value types; some common examples
include: int, float, and char. Value types are implemented as structs, and will always have a
default value. There are a few limitations to value types, value types cannot derive from each
other and they cannot have an explicit constructor. With value types, the instances of value types
are allocated on the stack so they are bound to their variables. This means that when the method
that the value type variable was defined in has finished executing the variable goes out of scope
and the value is discarded from the stack where it was allocated. This makes it where value types
have a limited lifetime and makes them inefficient for sharing data between different classes. It’s
also important to note that C# does not allow for global functions or variables. Instead of
allowing global functions and variables, the language allows programmers to use static members
of public classes instead.
5
Emilee King C# CSC 415
While instances of value types are saved on the stack, instances of a reference type are
saved in a different area of memory called the heap. A variable of a reference type actually just
“contains the address of a location in memory where the data referred to by that variable is stored
(Deitel & Deitel, 2005)” and when the variable is not referencing any object the variable will be
null. Unlike value types when a method finishes executing a variable for a reference type will not
be destroyed until C#’s garbage collection system determines that it is no longer needed. This
makes it where reference types can be used for sharing data between different classes. A few
examples of reference types are: dynamic, object, and string. It’s important to note that string is a
reference type and not a value type.
One of C#’s main goals was to provide unification, so it is not surprising that C# offered
programmers the option to have a unified type system. Before C# introduced generics to the
language the main way to achieve a unified type system was to use the process of boxing and
unboxing. Boxing is when a value type is converted into a reference type and unboxing is when a
reference type is converted into a value type. When boxing occurs in C# what is happening is
that an object instance is allocated on the heap and the value from the value type is copied into
the new object. During the unboxing process the object instance on the heap is checked is ensure
that it is a value that was created by previously boxing an instance of the given value type, if the
value types are the same and the value is not null then the value from the instance is copied into
the value type variable. A downfall to the boxing and unboxing process is that it is
computationally intensive, another issue with this process is that it offers no compile time type
checking which will lead to errors that can go undetected at compile time and have the potential
to continue going undetected until after release of an application. C# also offers the ability to use
6
Emilee King C# CSC 415
pointers to directly manipulate memory, however pointers can only be used inside of a block of
code that is marked with the unsafe keyword.
Generics were introduced in Version 2.0 of the .NET Framework. In comparison to the
boxing and unboxing process generics were able to “combine reusability, type safety and
efficiency in a way that their non-generic counterparts cannot. (Microsoft, 2013)” When using
generics a programmer can avoid the high computational cost of boxing and unboxing that was
mentioned earlier. As well as avoiding the computational cost of boxing and unboxing, generics
also make it possible for the compiler to do type checking which can help eliminate the type
errors in runtime that went undetected in boxing and unboxing.
C# also offers a variety of different arrays. Arrays in C# can be one dimensional,
multidimensional, or an array of arrays. Multidimensional arrays are also known as rectangular
arrays and an array of arrays is known as a jagged array. Arrays in C# are zero indexed and array
elements can be of any type. C# defaults elements of an array to zero if the array elements are
numeric and null if the array elements are reference.
Expressions and Assignment Statements
When it comes to expression operators C# has the standard list that many programmers
have come to expect in a language. C# also offers various unary, binary, comparison, assignment
cast, primary, shift, and array indexing operators.
Unary operators in C# include: +x, -x, !x, ~x, ++x, --x, (T)x.
The conditional operator “?:” is the single ternary operator in C#.
7
Emilee King C# CSC 415
Precedence
It is important for programmers in any language to know the operator precedence so
expressions can be written in a way to avoid unexpected results. The following is a list of C# in
order of precedence from highest to lowest:
1. Primary: x.y, f(x), a[x], x++, x--, new, typeof, checked, unchecked.
2. Unary: +, -, !, ~, ++x, --x, (T)x
3. Multiplicative: *, /, %
4. Additive: +, -
5. Shift: <<, >>
6. Relational and type testing: <, >, <=, >=, is, as,
7. Equality: ==, !=
8. Logical AND: &
9. Logical XOR: ^
10. Logical OR: |
11. Conditional AND: &&
12. Conditional OR: ||
13. Conditional: ?:
14. Assignment: =, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |=
(Microsoft, 2003)
Along with operator precedence, C# also has rules to handle when an operand occurs between
two operators with the same precedence called associativity. All binary operators, except
assignment operators, evaluate left to right meaning that x+y+z is the same as (x+y) + z. While
binary operators evaluate left to right, assignment operators and the conditional operator are
evaluated right to left. However, if a programmer would like an expression to be evaluated in a
way that does not follow the normal rules of precedence and associativity they can control the
way an expression is evaluated by using parentheses. An example of this would be x+y*z
8
Emilee King C# CSC 415
multiplies z and y before adding the product to x, by adding parentheses to the expression
(x+y)*z it makes it where x and y are added together and then that sum is multiplied with z.
Operator Overloading
C# allows programmers to be able to overload operators, this is where different operators
have different implementations depending on their arguments. In C# operators can be overloaded
by writing an overload method, not all operators can be overloaded however. C# allows unary,
binary, and comparison operators to overloaded. Cast operators and array indexing cannot be
overloaded, but with cast operators the programmer can define conversion operators and with
array indexing the programmer can define new indexers.
Statement Level Control Structures
Conditional Statements
C# has two different types of built in conditional statements that control the flow of the
program through given conditions, the if-else statement and the switch statement. C#’s if-else
statement is pretty straightforward and easy to read and write. C# allows single line ‘if-else’
statements to be written without block braces, although block braces are preferred by coding
conventions. If there are multiple ‘if-else’ statements without braces the code can become hard to
read. C# also allows ‘else-if’ statements that can be added on to ‘if’ statements, which can help
lessen the number of if statements needed while writing code. While the ‘if-else’ statement works
based on if a condition is true it executes what is inside the ‘if’ statement and if the condition is
false it executes what is inside the ‘else’ statement if one is provided, the switch statement acts as
a filter for different values. A value is given to the switch statement and from there that value
9
Emilee King C# CSC 415
leads to a case that is specific for the given value. Different cases can lead to the same code, and
if there is no specifically defined case to handle the value given the default case will handle it.
Iterative Statements
C# includes four different types of iterative statements: the while loop, the do while loop,
the for loop, and the foreach loop. The while statement is pretty straightforward, it continues to
loop until a specified expression is false. The do while loop is similar to the while loop except
the do while loop is executed one time before the conditional statement is evaluated. Also, the
conditional statement of the while loop is evaluated before the execution of each loop so the
while loop executes zero or more times while the do loop executes one of more times.
The for loop has three different parts: the declaration, the condition, and the increment.
The following is an outline for a for loop:
for(declaration; condition, iterator)
{
body;
}
The declaration statement only executes once and it initializes the condition. The condition part
contains a Boolean expression that will be determine whether or not the loop should run again or
exit. The iteration section defines what happens each time after the loop executes. Each part of
the for loop is optional, so a for loop can function correctly when one or more of the parts is left
out. While C# inherits most of its control structures from C and C++ it also adds on to the list by
including a new iterative statement called the foreach statement. The foreach statement is built
off of the for statement and is made to iterate through each element in an array or object
collection.
10
Emilee King C# CSC 415
Jump Statements
Branching is performed using jump statements, which cause an immediate transfer of the
program control (Microsoft , n.d.). Jump statements include: break, continue, and goto. These
jump statements are mainly used to exit out of conditional and iterative statements. The break
statement breaks out of the current loop or switch statement. The continue statement stops the
current iteration and begins the next iteration; and the goto jump statement transfers control to
another point in the code that has been labeled for a goto statement.
Subprograms
C# does not have subprograms, rather it has methods, structs, and classes. These methods
can have any type and this includes user defined types and void. These methods are used in
classes, and they define how a class should behave. Structs can have methods, however by
convention the use of methods in structs are generally discouraged. Structs and classes in C# use
similar syntax, and as discussed earlier in the data types section structs are value types while
classes are reference types. Classes are used for data that has the potential to be modified after
the class is created while structs are better suited for data that is not going to be modified after
the struct is created. Structs are limited when compared to classes, especially when it comes to
object oriented programming. This is because classes, unlike structs, support inheritance which is
a fundamental idea of object oriented programming. Another way that classes are used in object
oriented programming is that they have the ability to be nested. While C# allows for nested
classes, it does not allow for nested methods. C# also increases the power and flexibility of
methods by allowing them to be made into objects. This makes it where methods can be passed
as parameters, and when this happens these objects are called delegates.
11
Emilee King C# CSC 415
Abstract Data Types and Encapsulation Constructs
“Encapsulation and abstraction are an essential part of C# programming that is mostly
used to hide complex code from unauthorized user and shows only relevant information
(Concepts of Encapsulation and Abstraction (C#), n.d.)”. Abstract Data types in C# are mainly
based off of those from C++ and Java. Similar to Java, class instances in C# are all heap dynamic
and because of the way that C# uses its garbage collection for heap objects, destructors are rarely
used even though C# allows them to be defined. C# does add two new access modifiers, internal
and protected internal, as well as offers properties as a way of implementing getters and setters
without having to use explicit method calls. As mentioned earlier, C# has both classes and structs
which are the two main constructs that C# offers. While C++ also has the ability to use structs,
structs in C# are completely different and can be seen as lightweight classes. It is important note
that while structs do not support inheritance they can implement interfaces. Also, while classes
can only inherit from one other class they have the ability to implement more than one interface.
Support for Object-Oriented Programming
One of the driving factors that led to the creation of C# was to have a C based language
that was object oriented, so it’s not surprising that C# provides full support for object oriented
programming. C# fully supports encapsulation, inheritance, polymorphism, dynamic binding,
nested classes, and unlike many other languages adds the ability to use structs. C# also offers a
unified type system, interfaces, properties, events, and methods. All of which were included with
object oriented design in mind.
12
Emilee King C# CSC 415
Concurrency
While C# borrows a slight amount of its design from Java the creators of C# took every
opportunity they could to improve on it. One example of this would be threads in C#; while C#
threads are loosely based on Java's threads there are significant differences that improve the
fluidity of threads in C#. In both C# and Java there are two different types of threads: actors and
servers. One of the improvements that C# added when it came to threading is while Java only
supports actor threads C# supports both. C# focuses on two different built in classes, and one
statement to achieve thread synchronization: the Interlocked class, the Monitor class, and the
lock statement. C# makes threading more sophisticated by adding these built in options that are
designed for specific needs.
The Interlocked class is used mainly to prevent errors that happen when more than one
thread tries to update or compare the same value at the same time. The most used methods for the
Interlocked class are the Increment and Decrement methods, which take the value that is passed
into the class as a parameter and either increments the value or decrements it. While those are
the two most used there are twenty other methods in the Interlocked class including: Add,
MemoryBarrier, Exchange, and CompareExchange. This class allows the programmer to use a
reference to a value as a parameter and with these methods safely increment, decrement,
exchange, and compare that value from any thread.
The Monitor class is a class that provides a way to have more control over the
synchronization of threads by locking objects. This class has five pain parts: Enter/TryEnter,
Wait, Pulse/PulseAll, and Exit. Enter takes in an object reference as a parameter and acquires a
lock for that object while TryEnter will attempt to acquire a lock and will set a value that shows
13
Emilee King C# CSC 415
whether or not the lock was taken. Enter/TryEnter also marks the beginning of the critical
section, which is a block of code that the Monitor class will not allow any other thread to access
unless it is using a different locked object. Wait is used to release the lock on an object so that
other threads can access and lock the object, this also blocks the current thread until it reacquires
the lock. Pulse/PulseAll are used to notify threads that are waiting when there is a change in the
locked object's state and that the lock is ready to be released. Pulse is used to notify a thread that
is in the waiting queue while PulseAll notifies all waiting threads. Exit is used to release the lock
on an object and mark the end of the critical section that was protected by the locked object.
Similar to the Monitor class, the lock statement is used to prevent a block of code from
being accessed by more than one thread at a time. The lock statement takes in an object as an
argument and contains the block of code that can only be run by one thread at a time, it does not
have extra methods such as Wait and Pulse. While it seems that the Monitor class gives more
control over the synchronization of threads the lock statement is “generally preferred over using
the Monitor class directly, because lock is more concise, and because lock insures that the
underlying monitor is released, even if the protected code throws an exception. This is
accomplished with the finally keyword, which executes its associated code block regardless of
whether an exception is thrown (Microsoft, 2013).”
Exception Handling and Event Handling
Exception Handling
While making an application a programmer might be able to see where a problem could
potentially arise. When these potentially problematic situations are noticed the ability to be able
to handle exceptions that might happen is very useful. C# offers a large variety of options when
14
Emilee King C# CSC 415
it comes to exception handling. Exceptions in C# are always represented by classes which can be
predefined or user made. The language has a large amount of predefined exception classes
already such as: IOException which handles I/O errors, DivideByZero which handles division by
zero, and InvalidCastException which handles errors that can occur during typecasting. When a
more specific exception class is needed the programmer can create a new exception specific to
the program’s specific needs. All user defined exceptions are derived from the
ApplicationException class.
The syntax for exception handling in C# is built on four main keywords: try, catch,
finally, and throw. The try block is where the code where the potential problem could arise. The
catch block is used to specify what kind of exception to catch and what should happen if a
specific exception happens. Catch blocks are also known as exception filters because multiple
catch blocks can be used for the same try block for different types of specific exceptions that
might happen. The finally block is used to execute code regardless if in exception is thrown or
not, this is important in situations where resources in the code need to be released even
regardless of how the try block is exited.
Event Handling
C# has two different types of event handlers: Static and Dynamic. The static event
handlers are only in effect in the class of the events that they handle, while dynamic event
handlers are activated and deactivated throughout the entire program in response to their
conditional programming logic. Both types of handlers take in two parameters that are always of
type object and EventArgs. The object parameter refers to the instant that raised the event and the
EventArgs parameter holds the event data.
15
Emilee King C# CSC 415
Evaluation of C#
Readability
Overall C# is an intuitive language when it comes to readability. By allowing user
defined structs and enumerations it allows programmers to have the ability to name things in a
way that allows them to clearly label what their program is doing. C# also allows the ability to
use regions which allows the programmer to put blocks of code into an outline type layout. These
regions can be named whatever the programmer wants them to be named and this allows the
code to be read easier, especially when in long code files. There can be multiple regions in a file
and the regions can be collapsed or expended one or more at a time and this improves readability
by allowing the programmer to focus on the part of the file that they want to currently look at
while hiding the rest.
Writability
There are many different ways to accomplish the same task and get the same result in C#,
this influences the writability of C# in a positive way. As mentioned earlier C# is a very
intuitive language so it is naturally easy to pick up; however C#’s strict syntax while it increases
reliability can sometimes be a hindrance when learning the language. Overall writability in C#
can be a little difficult at first to new programmers, but C# has endless documentation online
especially on the Microsoft Developer Network which can help newcomers overcome initial
problems with writability.
16
Emilee King C# CSC 415
Reliability
“A program is said to be reliable if it performs to its specifications under all conditions
(Sebesta, 2012).” Overall C# has a large amount of built in features to make the language more
reliable. C# is a strongly typed language, and is strict when it comes to type checking. While C#
will allow unsafe code, the unsafe code has to be explicitly marked and enabled in the compiler
for that code to work. C# offers a large variety of options when it comes to exception handling,
not only with built in Exception classes but also user defined Exception classes. Readability and
writability also affect the reliability of a language in all cycles of a programs lifetime. As
mentioned above C# has efficient readability and writability, so this impacts the reliability by
making it easier to write, maintain, and modify.
17
Emilee King C# CSC 415
Works CitedConcepts of Encapsulation and Abstraction (C#). (n.d.). Retrieved from Complete C # Tutorial:
http://www.completecsharptutorial.com/basic/understanding-concepts.php
Deitel, H. M., & Deitel, P. J. (2005). C# for Programmers Second Edition. Upper Saddle River: Pearson Education, Inc.
Hamilton, N. (2008, October 1). The A-Z of Programming Languages: C#. Retrieved from Computer World: http://www.computerworld.com.au/article/261958/a-z_programming_languages_c_/
Microsoft . (n.d.). C# Keywords. Retrieved from Microsoft Developer Network: http://msdn.microsoft.com/en-us/library/x53a06bb.aspx
Microsoft. (2003). Operator Precedence and Associativity. Retrieved from Microsoft Developer Network: http://msdn.microsoft.com/en-us/library/aa691323(v=vs.71).aspx
Microsoft. (2013). Introduction to Generics (C# Programming Guide). Retrieved from Microsoft Developer Network: http://msdn.microsoft.com/en-us/library/0x6a29h6.aspx
Microsoft. (2013). Thread Synchronization. Retrieved from Microsoft Developer Network: http://msdn.microsoft.com/en-us/library/ms173179.aspx
Sebesta, R. W. (2012). Concepts of Programming Languages Tenth Edition. Upper Saddle River: Pearson Education.
Willis, T., & Newsome, B. (2008). Beginning Microsoft Visual Basic 2008. Indianapolis, IN: Wiley Publishing, Inc.
18