Security in C#

Security is an indispensable aspect of every C# applications, and must be considered at every phase of development: not merely when design and implementation are complete.

This list is in not an exhaustive list of potential security problems. It highlights some common issues of which C# developers need to be aware.

  • Use the checked keyword to control the overflow-checking context for integral-type arithmetic operations and conversions.

  • Always use the most restrictive data type for parameters. For example, when passing a value into a method that describes the size of a data structure, use unsigned integer rather than integer.

  • Do not make decisions based on filenames. Filenames can be expressed in many different ways, and your test for a particular file may be bypassed.

  • Never, ever hardcode passwords or other sensitive information into your application.

  • Always validate input that is used to generate SQL queries.

  • Validate all inputs into your methods. The regular expression methods in System.Text.RegularExpressions namespace are useful for confirming input is of the correct form, such as an email address.

  • Don't display exception information: it provides any would-be attacker with valuable clues.

  • Ensure your application works while running with the least possible privileges. Few applications require a user to be logged in as an administrator.

  • Don't use your own encryption algorithms, use the System.Security.Cryptography classes.

  • Give your Assemblies strong names.

  • Don't store sensitive information in XML or other configuration files.

  • Check managed code that wraps native code carefully. Confirm the native code is secure, especially with regard to buffer overruns.

  • Use caution when using delegates passed from outside your application.

  • Run FxCop on your assemblies to ensure compliance with Microsoft .NET Framework Design Guidelines. FxCop can also find and warn against over 200 code defects.

Reflection in C#

Reflection provides objects (of type Type) that encapsulate assemblies, modules and types. You can use reflection to dynamically create an instance of a type, bind the type to an existing object, or get the type from an existing object and invoke its methods or access its fields and properties. If you are using attributes in your code, Reflection enables you to access them. For more information, see Attributes.

Here's a simple example of Reflection using the static method GetType - inherited by all types from the Object base class - to obtain the type of a variable:

// Using GetType to obtain type information:
int i = 42;
System.Type type = i.GetType();
System.Console.WriteLine(type);

The output is:

System.Int32

In this example, Reflection is used to obtain the full name of a loaded assembly:

// Using Reflection to get information from an Assembly:
System.Reflection.Assembly o = System.Reflection.Assembly.Load("mscorlib.dll");
System.Console.WriteLine(o.GetName());

The output is:

mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

Reflection Overview

Reflection is useful in the following situations:

Performance in C#

This section covers two issues that can adversely effect performance, and links to resources on performance issues.

Boxing and Unboxing

Boxing and unboxing are computationally expensive processes. When a value type is boxed, an entirely new object must be created. This can take up to 20 times longer than an assignment. When unboxing, the casting process can take four times as long as an assignment. For more information, see Boxing and Unboxing.

Destructors

Empty destructors should not be used. When a class contains a destructor, an entry is created in the Finalize queue. When the destructor is called, the garbage collector is invoked to process the queue. If the destructor is empty, this simply results in a loss of performance. For more information, see Destructors.

Threading In C#

Threading enables your C# program to perform concurrent processing so you can do more than one operation at a time. For example, you can use threading to monitor input from the user, perform background tasks, and handle simultaneous streams of input. The System.Threading namespace provides classes and interfaces that support multithreaded programming and enable you to easily perform tasks such as creating and starting new threads, synchronizing multiple threads, suspending threads, and aborting threads.

To incorporate threading in your C# code, simply create a function to be executed outside the main thread and point a new Thread object at it. The following code example creates a new thread in a C# application

System.Threading.Thread newThread;
newThread = new System.Threading.Thread(anObject.AMethod);

The following code example starts a new thread in a C# application:

newThread.Start();

Multithreading solves problems with responsiveness and multi-tasking, but can also introduce resource sharing and synchronization issues because threads are interrupted and resumed without warning according to a central thread scheduling mechanism. For more information, see Thread Synchronization. See Using Threads and Threading for overview information.

Threads have the following properties:

  • Threads enable your C# program to perform concurrent processing.

  • The .NET Framework's System.Threading namespace makes using threads easier.

  • Threads share the application's resources. For more information, see Using Threads and Threading.

Interoperability in C#

Interoperability enables you to preserve and leverage existing investments in unmanaged code. Code running under the control of the common language runtime (CLR) is called "managed code", and code running outside the CLR is "unmanaged code". COM, COM+, C++ components, ActiveX components, and Win32 API are examples of unmanaged code.

The .NET Framework enables interoperability with unmanaged code through platform invoke services, the System.Runtime.InteropServices namespace, and the CLR and through COM Interoperability (COM interop).

  • There are two ways to use unmanaged APIs from managed code: through platform invoke and through It Just Works (IJW) in C++. Platform invoke enables managed code to call functions exported from an unmanaged dynamic link library (DLL), such as Win32 API and custom DLLs. The CLR handles DLL loading and parameter marshaling. For performance, check if there is an equivalent function available in the .NET Framework rather than using platform invoke. For more information, see A Closer Look at Platform Invoke.

  • COM interop, which enables managed code to interact with COM objects through COM interfaces and COM clients. There are two ways to use COM components from managed code:

    • For calling OLE Automation compatible COM components, use COM interop or tlbimp.exe. The CLR handles COM component activation and parameter marshaling.

    • For IDL based COM components, use IJW and C++. Every public managed class that implements IUnknown, IDispatch, and other standard COM interfaces can be called from unmanaged code through COM interop. For more information, see Microsoft .NET/COM Migration and Interoperability.

For more information, see Interoperating with Unmanaged Code and Improving Interop Performance.

Both PInvoke and COM interop use marshaling to translate arguments such as integers, strings, arrays, structures, and pointers between managed and unmanaged code. For more information, see Interop Marshaling Overview.

Exceptions and Exception Handling in C#

The C# language's exception handling features provide a way to deal with any unexpected or exceptional situations that arise while a program is running. Exception handling uses the try, catch, and finally keywords to attempt actions that may not succeed, to handle failures, and to clean up resources afterwards. Exceptions can be generated by the common language runtime (CLR), by third-party libraries, or by the application code using the throw keyword.

In this example, a method tests for a division by zero, and catches the error. Without the exception handling, this program would terminate with a DivideByZeroException was unhandled error.

int SafeDivision(int x, int y)
{
try
{
return (x / y);
}
catch (System.DivideByZeroException dbz)
{
System.Console.WriteLine("Division by zero attempted!");
return 0;
}
}

Exceptions have the following properties:

  • When your application encounters an exceptional circumstance, such as a division by zero or low memory warning, an exception is generated.

  • Use a try block around the statements that might throw exceptions.

  • Once an exception occurs within the try block, the flow of control immediately jumps to an associated exception handler, if one is present.

  • If no exception handler for a given exception is present, the program stops executing with an error message.

  • If a catch block defines an exception variable, you can use it to get more information on the type of exception that occurred.

  • Actions that may result in an exception are executed with the try keyword.

  • An exception handler is a block of code that is executed when an exception occurs. In C#, the catch keyword is used to define an exception handler.

  • Exceptions can be explicitly generated by a program using the throw keyword.

  • Exception objects contain detailed information about the error, including the state of the call stack and a text description of the error.

  • Code in a finally block is executed even if an exception is thrown, thus allowing a program to release resources.

Collection Classes in C#

The .NET Framework provides specialized classes for data storage and retrieval. These classes provide support for stacks, queues, lists, and hash tables. Most collection classes implement the same interfaces, and these interfaces may be inherited to create new collection classes that fit more specialized data storage needs.

NoteNote

Applications that target version 2.0 and later of the .NET Framework should use the generic collection classes in the System.Collections.Generic namespace, which provide greater type-safety and efficiency than their non-generic counterparts.

C#
ArrayList list = new ArrayList();
list.Add(10);
list.Add(20);

Collection Classes Overview

Collection Classes have the following properties

  • Collection classes are defined as part of the System.Collections or System.Collections.Generic namespace.

  • Most collection classes derive from the interfaces ICollection, IComparer, IEnumerable, IList, IDictionary, and IDictionaryEnumerator and their generic equivalents.

  • Using generic collection classes provides increased type-safety and in some cases can provide better performance, especially when storing value types. For more information, see Benefits of Generics.

Attributes in C#

Attributes provide a powerful method of associating declarative information with C# code (types, methods, properties, and so forth). Once associated with a program entity, the attribute can be queried at run time using a technique called Reflection.

Attributes exist in two forms: attributes that are defined in the Common Language Runtime's base class library and custom attributes that you can create, to add extra information to your code. This information can later be retrieved programmatically.

In this example, the attribute System.Reflection.TypeAttributes.Serializable is used to apply a specific characteristic to a class:

C#
[System.Serializable]
public class SampleClass
{
// Objects of this type can be serialized.
}

Attribute Overview

Attributes have the following properties:

  • Attributes add metadata to your program. Metadata is information embedded in your program such as compiler instructions or descriptions of data.

  • Your program can examine its own metadata using Reflection. See Accessing Attributes With Reflection.

  • Attributes are commonly used when interacting with COM.

Assemblies and the Global Assembly Cache in C#

An assembly is a fundamental building block of any .NET Framework application. For example, when you build a simple C# application, Visual Studio creates an assembly in the form of a single portable executable (PE) file, specifically an EXE or DLL.

Assemblies contain metadata that describe their own internal version number and details of all the data and object types they contain. For more information see Assembly Manifest.

Assemblies are only loaded as they are required. If they are not used, they are not loaded. This means that assemblies can be an efficient way to manage resources in larger projects.

Assemblies can contain one or more modules. For example, larger projects may be planned in such a way that several individual developers work on separate modules, all coming together to create a single assembly. For more information on modules, see the topic How to: Build a Multifile Assembly.

Assemblies Overview

Assemblies have the following properties:

  • Assemblies are implemented as .exe or .dll files.

  • You can share an assembly between applications by placing it in the Global Assembly Cache.

  • Assemblies must be strong-named before they can be placed in the Global Assembly Cache. For more information, see Strong-Named Assemblies.

  • Assemblies are only loaded into memory if they are required.

  • You can programmatically obtain information about an assembly using reflection. For more information, see the topic Reflection.

  • If you want to load an assembly only to inspect it, use a method such as ReflectionOnlyLoadFrom.

  • You can use two versions of the same assembly in a single application. For more information, see extern alias.

Application Domains in C#

Application domains provide a flexible and secure method of isolating running applications.

Application domains are usually created and manipulated by run-time hosts. Occasionally, you may want your application to programmatically interact with your application domains, for example, to unload a component without having to stop your application from running.

Application domains aid security, separating applications from each other and each other's data. A single process can run several application domains, with the same level of isolation that would exist in separate processes. Running multiple applications within a single process increases server scalability.

In the following code example, you create a new application domain and then load and execute a previously built assembly, HelloWorld.exe, that is stored on drive C.

C#
static void Main()
{
// Create an Application Domain:
System.AppDomain newDomain = System.AppDomain.CreateDomain("NewApplicationDomain");

// Load and execute an assembly:
newDomain.ExecuteAssembly(@"c:\HelloWorld.exe");

// Unload the application domain:
System.AppDomain.Unload(newDomain);
}

Application domains have the following properties:

  • An assembly must be loaded into an application domain before it can be executed. For more information, see Assemblies and the Global Assembly Cache (C# Programming Guide).

  • Faults in one application domain cannot affect other code running in another application domain.

  • Individual applications can be stopped and code unloaded without stopping the entire process. You cannot unload individual assemblies or types, only entire application domains.

XML Documentation Comments in C#

In Visual C# you can create documentation for your code by including XML tags in special comment fields in your source code directly before the code block they refer to. For example:

/// 
/// This class performs an important function.
///

public class MyClass{}

When you compile with /doc the compiler will search for all XML tags in your source code and create an XML documentation file.

NoteNote

The XML doc comments are not metadata; they are not included in the compiled assembly and therefore they are not accessible through reflection.

Unsafe Code and Pointers in C#

Unsafe Code and Pointers

To maintain type safety and security, by default C# does not support pointer arithmetic. However, by using the unsafe keyword, it is possible to define an unsafe context in which pointers can be used. For more information about pointers, see the topic Pointer types.

NoteNote

In the common language runtime (CLR), unsafe code is referred to as unverifiable code. Unsafe code in C# is not necessarily dangerous; it is simply code whose safety cannot be verified by the CLR. The CLR will therefore only execute unsafe code if it is within a fully trusted assembly. If you use unsafe code, it is your responsibility to ensure that your code does not introduce security risks or pointer errors. For more information, see Security and C#.

Unsafe Code Overview

Unsafe code has the following properties:

  • Methods, types, and code blocks can be defined as unsafe.

  • In some cases, unsafe code may increase an application's performance by removing array bounds checks.

  • Unsafe code is required when calling native functions that require pointers.

  • Using unsafe code introduces security and stability risks.

  • In order for C# to compile unsafe code, the application must be compiled with /unsafe.

Nullable Types in C#

Nullable Types

Nullable types are instances of the System.Nullable struct. A nullable type can represent the normal range of values for its underlying value type, plus an additional null value. For example, a Nullable, pronounced "Nullable of Int32," can be assigned any value from -2147483648 to 2147483647, or it can be assigned the null value. A Nullable can be assigned the values true or false, or null. The ability to assign null to numeric and Boolean types is particularly useful when dealing with databases and other data types containing elements that may not be assigned a value. For example, a Boolean field in a database can store the values true or false, or it may be undefined.
C#
Copy Code

class NullableExample
{
static void Main()
{
int? num = null;
if (num.HasValue == true)
{
System.Console.WriteLine("num = " + num.Value);
}
else
{
System.Console.WriteLine("num = Null");
}

//y is set to zero
int y = num.GetValueOrDefault();

// num.Value throws an InvalidOperationException if num.HasValue is false
try
{
y = num.Value;
}
catch (System.InvalidOperationException e)
{
System.Console.WriteLine(e.Message);
}
}
}

The above will display the output:

num = Null

Nullable object must have a value.
Nullable Types Overview

Nullable types have the following characteristics:

*

Nullable types represent value-type variables that can be assigned the value of null. You cannot create a nullable type based on a reference type. (Reference types already support the null value.)
*

The syntax T? is shorthand for System.Nullable, where T is a value type. The two forms are interchangeable.
*

Assign a value to a nullable type in the same way as for an ordinary value type, for example int? x = 10; or double? d = 4.108;
*

Use the System.Nullable.GetValueOrDefault property to return either the assigned value, or the default value for the underlying type if the value is null, for example int j = x.GetValueOrDefault();
*

Use the HasValue and Value read-only properties to test for null and retrieve the value, for example if(x.HasValue) j = x.Value;
o

The HasValue property returns true if the variable contains a value, or false if it is null.
o

The Value property returns a value if one is assigned, otherwise a System.InvalidOperationException is thrown.
o

The default value for a nullable type variable sets HasValue to false. The Value is undefined.
*

Use the ?? operator to assign a default value that will be applied when a nullable type whose current value is null is assigned to a non-nullable type, for example int? x = null; int y = x ?? -1;

Namespaces in C#

Namespaces

Namespaces are heavily used in C# programming in two ways. First, the .NET Framework uses namespaces to organize its many classes, as follows:

C#

System.Console.WriteLine("Hello World!");

System is a namespace and Console is a class contained within that namespace. The using keyword can be used so that the entire name is not required, like this:

C#

using System;
C#

Console.WriteLine("Hello");
Console.WriteLine("World!");

For more information, see the topic using Directive (C# Reference).

Second, declaring your own namespaces can help you control the scope of class and method names in larger programming projects. Use the namespace keyword to declare a namespace, as in the following example:

C#

namespace SampleNamespace
{
class SampleClass
{
public void SampleMethod()
{
System.Console.WriteLine(
"SampleMethod inside SampleNamespace");
}
}
}

Namespaces Overview

A namespace has the following properties:

  • They organize large code projects.

  • They are delimited with the . operator.

  • The using directive means you do not need to specify the name of the namespace for every class.

  • The global namespace is the "root" namespace: global::system will always refer to the .NET Framework namespace System.

Iterators in C#

Iterators

Iterators are a new feature in C# 2.0. An iterator is a method, get accessor or operator that enables you to support foreach iteration in a class or struct without having to implement the entire IEnumerable interface. Instead, you provide just an iterator, which simply traverses the data structures in your class. When the compiler detects your iterator, it will automatically generate the Current, MoveNext and Dispose methods of the IEnumerable or IEnumerable interface.

Iterators Overview

  • An iterator is a section of code that returns an ordered sequence of values of the same type.

  • An iterator can be used as the body of a method, an operator, or a get accessor.

  • The iterator code uses the yield return statement to return each element in turn. yield break ends the iteration. For more information, see yield.

  • Multiple iterators can be implemented on a class. Each iterator must have a unique name just like any class member, and can be invoked by client code in a foreach statement as follows: foreach(int x in SampleClass.Iterator2){}

  • The return type of an iterator must be IEnumerable, IEnumerator, IEnumerable, or IEnumerator.

The yield keyword is used to specify the value, or values, returned. When the yield return statement is reached, the current location is stored. Execution is restarted from this location the next time the iterator is called.

Iterators are especially useful with collection classes, providing an easy way to iterate non-trivial data structures such as binary trees.

Related Sections

Example

In this example, the class DaysOfTheWeek is a simple collection class that stores the days of the week as strings. After each iteration of a foreach loop, the next string in the collection is returned.

C#

public class DaysOfTheWeek : System.Collections.IEnumerable
{
string[] m_Days = { "Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat" };

public System.Collections.IEnumerator GetEnumerator()
{
for (int i = 0; i < m_Days.Length; i++)
{
yield return m_Days[i];
}
}
}

class TestDaysOfTheWeek
{
static void Main()
{
// Create an instance of the collection class
DaysOfTheWeek week = new DaysOfTheWeek();

// Iterate with foreach
foreach (string day in week)
{
System.Console.Write(day + " ");
}
}
}

Output

Sun Mon Tue Wed Thr Fri Sat

Generics in C#

Generics

Generics are a new feature in version 2.0 of the C# language and the common language runtime (CLR). Generics introduce to the .NET Framework the concept of type parameters, which make it possible to design classes and methods that defer the specification of one or more types until the class or method is declared and instantiated by client code. For example, by using a generic type parameter T you can write a single class that other client code can use without incurring the cost or risk of runtime casts or boxing operations, as shown here:

C#

// Declare the generic class
public class GenericList
{
void Add(T input) { }
}
class TestGenericList
{
private class ExampleClass { }
static void Main()
{
// Declare a list of type int
GenericList<int> list1 = new GenericList<int>();

// Declare a list of type string
GenericList<string> list2 = new GenericList<string>();

// Declare a list of type ExampleClass
GenericList list3 = new GenericList();
}
}

Generics Overview

  • Use generic types to maximize code reuse, type safety, and performance.

  • The most common use of generics is to create collection classes.

  • The .NET Framework class library contains several new generic collection classes in the System.Collections.Generic namespace. These should be used whenever possible in place of classes such as ArrayList in the System.Collections namespace.

  • You can create your own generic interfaces, classes, methods, events and delegates.

  • Generic classes may be constrained to enable access to methods on particular data types.

  • Information on the types used in a generic data type may be obtained at run-time by means of reflection.

Events in C#

Events

Events provide a way for a class or object to notify other classes or objects when something of interest happens. The class that sends (or raises) the event is called the publisher and the classes that receive (or handle) the event are called subscribers.

In a typical C# Windows Forms or Web application, you subscribe to events raised by controls such as buttons and list boxes. You can use the Visual C# integrated development environment (IDE) to browse the events that a control publishes and select the ones that you want to handle. The IDE automatically adds an empty event handler method and the code to subscribe to the event. For more information, see How to: Subscribe to and Unsubscribe from Events (C# Programming Guide).

Events Overview

Events have the following properties:

  • The publisher determines when an event is raised; the subscribers determine what action is taken in response to the event.

  • An event can have multiple subscribers. A subscriber can handle multiple events from multiple publishers.

  • Events that have no subscribers are never called.

  • Events are commonly used to signal user actions such as button clicks or menu selections in graphical user interfaces.

  • When an event has multiple subscribers, the event handlers are invoked synchronously when an event is raised. To invoke events asynchronously, see Calling Synchronous Methods Asynchronously.

  • Events can be used to synchronize threads.

  • In the .NET Framework class library, events are based on the EventHandler delegate and the EventArgs base class.

Delegates In C#

Delegates

A delegate is a type that references a method. Once a delegate is assigned a method, it behaves exactly like that method. The delegate method can be used like any other method, with parameters and a return value, as in this example:

C#
public delegate int PerformCalculation(int x, int y);

Any method that matches the delegate's signature, which consists of the return type and parameters, can be assigned to the delegate. This makes is possible to programmatically change method calls, and also plug new code into existing classes. As long as you know the delegate's signature, you can assign your own delegated method.

This ability to refer to a method as a parameter makes delegates ideal for defining callback methods. For example, a sort algorithm could be passed a reference to the method that compares two objects. Separating the comparison code allows the algorithm to be written in a more general way.

Delegates Overview

Delegates have the following properties:

  • Delegates are similar to C++ function pointers, but are type safe.

  • Delegates allow methods to be passed as parameters.

  • Delegates can be used to define callback methods.

  • Delegates can be chained together; for example, multiple methods can be called on a single event.

  • Methods don't need to match the delegate signature exactly. For more information, see Covariance and Contravariance

  • C# version 2.0 introduces the concept of Anonymous Methods, which permit code blocks to be passed as parameters in place of a separately defined method.

Indexers in C#

Indexers

Indexers permit instances of a class or struct to be indexed in the same way as arrays. Indexers are similar to properties except that their accessors take parameters.

In the following example, a generic class is defined and provided with simple get and set accessor methods as a means for assigning and retrieving values. The class Program creates an instance of this class for storing strings.

C#
class SampleCollection
{
private T[] arr = new T[100];
public T this[int i]
{
get
{
return arr[i];
}
set
{
arr[i] = value;
}
}
}

// This class shows how client code uses the indexer
class Program
{
static void Main(string[] args)
{
SampleCollection<string> stringCollection = new SampleCollection<string>();
stringCollection[0] = "Hello, World";
System.Console.WriteLine(stringCollection[0]);
}
}
  • Indexers enable objects to be indexed in a similar way to arrays.

  • A get accessor returns a value. A set accessor assigns a value.

  • The this keyword is used to define the indexers.

  • The value keyword is used to define the value being assigned by the set indexer.

  • Indexers do not have to be indexed by an integer value; it is up to you how to define the specific look-up mechanism.

  • Indexers can be overloaded.

  • Indexers can have more than one formal parameter, for example, when accessing a two-dimensional array.

Properties In C#

Properties

Properties are members that provide a flexible mechanism to read, write, or compute the values of private fields. Properties can be used as though they are public data members, but they are actually special methods called accessors. This enables data to be accessed easily while still providing the safety and flexibility of methods.

In this example, the class TimePeriod stores a time period. Internally the class stores the time in seconds, but a property called Hours is provided that allows a client to specify a time in hours. The accessors for the Hours property perform the conversion between hours and seconds.

C#

class TimePeriod
{
private double seconds;

public double Hours
{
get { return seconds / 3600; }
set { seconds = value * 3600; }
}
}

class Program
{
static void Main()
{
TimePeriod t = new TimePeriod();

// Assigning the Hours property causes the 'set' accessor to be called.
t.Hours = 24;

// Evaluating the Hours property causes the 'get' accessor to be called.
System.Console.WriteLine("Time in hours: " + t.Hours);
}
}

Time in hours: 24

  • Properties enable a class to expose a public way of getting and setting values, while hiding implementation or verification code.

  • A get property accessor is used to return the property value, and a set accessor is used to assign a new value. These accessors can have different access levels. For more information, see Accessor Accessibility.

  • The value keyword is used to define the value being assigned by the set indexer.

  • Properties that do not implement a set method are read only.

C# TIPS

How to: Know the Difference Between Passing a Struct and Passing a Class Reference to a Method (C# Programming Guide)

This example shows that when a struct is passed to a method, a copy of the struct is passed, but when a class instance is passed, a reference is passed.

The output of the following example shows that only the value of the class field is changed when the class instance is passed to the ClassTaker method. The struct field, however, does not change by passing its instance to the StructTaker method. This is because a copy of the struct is passed to the StructTaker method, while a reference to the class is passed to the ClassTaker method.

Example

C#

class TheClass
{
public string willIChange;
}

struct TheStruct
{
public string willIChange;
}

class TestClassAndStruct
{
static void ClassTaker(TheClass c)
{
c.willIChange = "Changed";
}

static void StructTaker(TheStruct s)
{
s.willIChange = "Changed";
}

static void Main()
{
TheClass testClass = new TheClass();
TheStruct testStruct = new TheStruct();

testClass.willIChange = "Not Changed";
testStruct.willIChange = "Not Changed";

ClassTaker(testClass);
StructTaker(testStruct);

System.Console.WriteLine("Class field = {0}", testClass.willIChange);
System.Console.WriteLine("Struct field = {0}", testStruct.willIChange);
}
}

Output

Class field = Changed
Struct field = Not Changed

Static Classes and Static Class Members in C#

Static Classes and Static Class Members

Static classes and class members are used to create data and functions that can be accessed without creating an instance of the class. Static class members can be used to separate data and behavior that is independent of any object identity: the data and functions do not change regardless of what happens to the object. Static classes can be used when there is no data or behavior in the class that depends on object identity.

A class can be declared static, indicating that it contains only static members. It is not possible to create instances of a static class using the new keyword. Static classes are loaded automatically by the .NET Framework common language runtime (CLR) when the program or namespace containing the class is loaded.

Use a static class to contain methods that are not associated with a particular object. For example, it is a common requirement to create a set of methods that do not act on instance data and are not associated to a specific object in your code. You could use a static class to hold those methods.

The main features of a static class are:

Creating a static class is therefore much the same as creating a class that contains only static members and a private constructor. A private constructor prevents the class from being instantiated.

The advantage of using a static class is that the compiler can check to make sure that no instance members are accidentally added. The compiler will guarantee that instances of this class cannot be created.

Static classes are sealed and therefore cannot be inherited. Static classes cannot contain a constructor, although it is still possible to declare a static constructor to assign initial values or set up some static state. For more information, see Static Constructors (C# Programming Guide).

Suppose you have a class CompanyInfo that contains the following methods to get information about the company name and address.

C#

class CompanyInfo
{
public string GetCompanyName() { return "CompanyName"; }
public string GetCompanyAddress() { return "CompanyAddress"; }
//...
}

These methods do not need to be attached to a specific instance of the class. Therefore, instead of creating unnecessary instances of this class, you can declare it as a static class, like this:

C#

static class CompanyInfo
{
public static string GetCompanyName() { return "CompanyName"; }
public static string GetCompanyAddress() { return "CompanyAddress"; }
//...
}

Use a static class as a unit of organization for methods not associated with particular objects. Also, a static class can make your implementation simpler and faster because you do not have to create an object in order to call its methods. It is useful to organize the methods inside the class in a meaningful way, such as the methods of the Math class in the System namespace.

A static method, field, property, or event is callable on a class even when no instance of the class has been created. If any instances of the class are created, they cannot be used to access the static member. Only one copy of static fields and events exists, and static methods and properties can only access static fields and static events. Static members are often used to represent data or calculations that do not change in response to object state; for instance, a math library might contain static methods for calculating sine and cosine.

Static class members are declared using the static keyword before the return type of the member, for example:

C#

public class Automobile
{
public static int NumberOfWheels = 4;
public static int SizeOfGasTank
{
get
{
return 15;
}
}
public static void Drive() { }
public static event EventType RunOutOfGas;

//other non-static fields and properties...
}

Static members are initialized before the static member is accessed for the first time, and before the static constructor, if any is called. To access a static class member, use the name of the class instead of a variable name to specify the location of the member. For example:

C#

Automobile.Drive();
int i = Automobile.NumberOfWheels;

Here is an example of a static class that contains two methods that convert temperature from Celsius to Fahrenheit and vice versa:

public static class TemperatureConverter
{
public static double CelsiusToFahrenheit(string temperatureCelsius)
{
// Convert argument to double for calculations.
double celsius = System.Double.Parse(temperatureCelsius);

// Convert Celsius to Fahrenheit.
double fahrenheit = (celsius * 9 / 5) + 32;

return fahrenheit;
}

public static double FahrenheitToCelsius(string temperatureFahrenheit)
{
// Convert argument to double for calculations.
double fahrenheit = System.Double.Parse(temperatureFahrenheit);

// Convert Fahrenheit to Celsius.
double celsius = (fahrenheit - 32) * 5 / 9;

return celsius;
}
}

class TestTemperatureConverter
{
static void Main()
{
System.Console.WriteLine("Please select the convertor direction");
System.Console.WriteLine("1. From Celsius to Fahrenheit.");
System.Console.WriteLine("2. From Fahrenheit to Celsius.");
System.Console.Write(":");

string selection = System.Console.ReadLine();
double F, C = 0;

switch (selection)
{
case "1":
System.Console.Write("Please enter the Celsius temperature: ");
F = TemperatureConverter.CelsiusToFahrenheit(System.Console.ReadLine());
System.Console.WriteLine("Temperature in Fahrenheit: {0:F2}", F);
break;

case "2":
System.Console.Write("Please enter the Fahrenheit temperature: ");
C = TemperatureConverter.FahrenheitToCelsius(System.Console.ReadLine());
System.Console.WriteLine("Temperature in Celsius: {0:F2}", C);
break;

default:
System.Console.WriteLine("Please select a convertor.");
break;
}
}
}

2

98.6

Please select the convertor

1. From Celsius to Fahrenheit.

2. From Fahrenheit to Celsius.

:2

Please enter the Fahrenheit temperature: 98.6

Temperature in Celsius: 37.00

Additional sample output might look as follows:

Please select the convertor

1. From Celsius to Fahrenheit.

2. From Fahrenheit to Celsius.

:1

Please enter the Celsius temperature: 37.00

Temperature in Fahrenheit: 98.60

Partial Class Definitions In C#

Partial Class Definitions

It is possible to split the definition of a class or a struct, or an interface over two or more source files. Each source file contains a section of the class definition, and all parts are combined when the application is compiled. There are several situations when splitting a class definition is desirable:

  • When working on large projects, spreading a class over separate files allows multiple programmers to work on it simultaneously.

  • When working with automatically generated source, code can be added to the class without having to recreate the source file. Visual Studio uses this approach when creating Windows Forms, Web Service wrapper code, and so on. You can create code that uses these classes without having to edit the file created by Visual Studio.

  • To split a class definition, use the partial keyword modifier, as shown below:

C#
public partial class Employee
{
public void DoWork()
{
}
}

public partial class Employee
{
public void GoToLunch()
{
}
}

Using the partial keyword indicates that other parts of the class, struct, or interface can be defined within the namespace. All the parts must use the partial keyword. All of the parts must be available at compile time to form the final type. All the parts must have the same accessibility, such as public, private, and so on.

If any of the parts are declared abstract, then the entire type is considered abstract. If any of the parts are declared sealed, then the entire type is considered sealed. If any of the parts declare a base type, then the entire type inherits that class.

All of the parts that specify a base class must agree, but parts that omit a base class still inherit the base type. Parts can specify different base interfaces, and the final type implements all of the interfaces listed by all of the partial declarations. Any class, struct, or interface members declared in a partial definition are available to all of the other parts. The final type is the combination of all the parts at compile time.

NoteNote

The partial modifier is not available on delegate or enumeration declarations.

Nested types can be partial, even if the type they are nested within is not partial itself. For example:

C#

class Container
{
partial class Nested
{
void Test() { }
}
partial class Nested
{
void Test2() { }
}
}
  • At compile time, attributes of partial-type definitions are merged. For example, the following declarations:

C#

[System.SerializableAttribute]
partial class Moon { }

[System.ObsoleteAttribute]
partial class Moon { }

are equivalent to:

C#

[System.SerializableAttribute]
[System.ObsoleteAttribute]
class Moon { }
  • The following are merged together from all the partial-type definitions:

  • XML comments

  • interfaces

  • generic-type parameter attributes

  • class attributes

  • members

For example, the following declarations:

C#

partial class Earth : Planet, IRotate { }
partial class Earth : IRevolve { }

are equivalent to:

C#

class Earth : Planet, IRotate, IRevolve { }

There are several rules to follow when working with partial class definitions:

  • All partial-type definitions meant to be parts of the same type must be modified with partial. For example, the following class declarations generate an error:

    C#

    public partial class A { }
    //public class A { } // Error, must also be marked partial
  • The partial modifier can only appear immediately before the keywords class, struct, or interface.

  • Nested partial types are allowed in partial-type definitions, for example:

    C#

    partial class ClassWithNestedClass
    {
    partial class NestedClass { }
    }

    partial class ClassWithNestedClass
    {
    partial class NestedClass { }
    }
  • All partial-type definitions meant to be parts of the same type must be defined in the same assembly and the same module (.exe or .dll file). Partial definitions cannot span multiple modules.

  • The class name and generic-type parameters must match on all partial-type definitions. Generic types can be partial. Each partial declaration must use the same parameter names in the same order.

  • The following keywords on a partial-type definition are optional, but if present on one partial-type definition, cannot conflict with the keywords specified on another partial definition for the same type:

In the following example, the fields and the constructor of the class, CoOrds, are declared in one partial class definition, while the member, PrintCoOrds, is declared in another partial class definition.

C#

public partial class CoOrds
{
private int x;
private int y;

public CoOrds(int x, int y)
{
this.x = x;
this.y = y;
}
}

public partial class CoOrds
{
public void PrintCoOrds()
{
System.Console.WriteLine("CoOrds: {0},{1}", x, y);
}

}

class TestCoOrds
{
static void Main()
{
CoOrds myCoOrds = new CoOrds(10, 15);
myCoOrds.PrintCoOrds();
}
}

CoOrds: 10,15

The following example shows that you can also develop partial structs and interfaces.

C#

partial interface ITest
{
void Interface_Test();
}

partial interface ITest
{
void Interface_Test2();
}

partial struct S1
{
void Struct_Test() { }
}

partial struct S1
{
void Struct_Test2() { }
}