Sunday, November 14, 2010

Lesson 51 - Application Domains

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.

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);
}

Lesson 49 - Use a Dictionary to Store Event Instances

One use for accessor-declarations is to expose a large number of events without allocating a field for each event, but instead using a Dictionary to store the event instances. This is only useful if you have a very large number of events, but you expect most of the events will not be implemented.

public delegate void EventHandler1(int i);
public delegate void EventHandler2(string s);

public class PropertyEventsSample
{
private System.Collections.Generic.Dictionary eventTable;

public PropertyEventsSample()
{
eventTable = new System.Collections.Generic.Dictionary();
eventTable.Add("Event1", null);
eventTable.Add("Event2", null);
}

public event EventHandler1 Event1
{
add
{
lock (eventTable)
{
eventTable["Event1"] = (EventHandler1)eventTable["Event1"] + value;
}
}
remove
{
lock (eventTable)
{
eventTable["Event1"] = (EventHandler1)eventTable["Event1"] - value;
}
}
}

public event EventHandler2 Event2
{
add
{
lock (eventTable)
{
eventTable["Event2"] = (EventHandler2)eventTable["Event2"] + value;
}
}
remove
{
lock (eventTable)
{
eventTable["Event2"] = (EventHandler2)eventTable["Event2"] - value;
}
}
}

internal void RaiseEvent1(int i)
{
EventHandler1 handler1;
if (null != (handler1 = (EventHandler1)eventTable["Event1"]))
{
handler1(i);
}
}

internal void RaiseEvent2(string s)
{
EventHandler2 handler2;
if (null != (handler2 = (EventHandler2)eventTable["Event2"]))
{
handler2(s);
}
}
}

public class TestClass
{
public static void Delegate1Method(int i)
{
System.Console.WriteLine(i);
}

public static void Delegate2Method(string s)
{
System.Console.WriteLine(s);
}

static void Main()
{
PropertyEventsSample p = new PropertyEventsSample();

p.Event1 += new EventHandler1(TestClass.Delegate1Method);
p.Event1 += new EventHandler1(TestClass.Delegate1Method);
p.Event1 -= new EventHandler1(TestClass.Delegate1Method);
p.RaiseEvent1(2);

p.Event2 += new EventHandler2(TestClass.Delegate2Method);
p.Event2 += new EventHandler2(TestClass.Delegate2Method);
p.Event2 -= new EventHandler2(TestClass.Delegate2Method);
p.RaiseEvent2("TestString");

Console.ReadLine();
}
}

Lesson 48 - 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.

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.

Lesson 47 - 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.

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);
}
}
}


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;

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

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

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;

Nested nullable types are not allowed. The following line will not compile: Nullable> n;

Lesson 46 - 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.

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.
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 + " ");
}
}
}

Lesson 45 - 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:

// 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 list1 = new GenericList();

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

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

Lesson 44 - Implement Interface Events

An interface can declare an event. The following example shows how to implement interface events in a class. Basically the rules are the same as when implementing any interface method or property.

To implement interface events in a class

Declare the event in your class and then invoke it in the appropriate places.

Copy
public interface IDrawingObject
{
event EventHandler ShapeChanged;
}
public class MyEventArgs : EventArgs {…}
public class Shape : IDrawingObject
{
event EventHandler ShapeChanged;
void ChangeShape()
{
// Do something before the event…
OnShapeChanged(new MyEventsArgs(…));
// or do something after the event.
}
protected virtual void OnShapeChanged(MyEventArgs e)
{
if(ShapeChanged != null)
{
ShapeChanged(this, e);
}
}
}

The following example shows how to handle the less-common situation in which your class inherits from two or more interfaces and each interface has an event with the same name. In this situation, you must provide an explicit interface implementation for at least one of the events. When you write an explicit interface implementation for an event, you must also write the add and remove event accessors. Normally these are provided by the compiler, but in this case the compiler cannot provide them.

By providing your own accessors, you can specify whether the two events are represented by the same event in your class, or by different events. For example, if the events should be raised at different times according to the interface specifications, then you can associate each event with a separate implementation in your class. In the following example, subscribers determine which OnDraw event they will receive by casting the shape reference to either an IShape or an IDrawingObject.

namespace WrapTwoInterfaceEvents
{
using System;

public interface IDrawingObject
{
// Raise this event before drawing
// the object.
event EventHandler OnDraw;
}
public interface IShape
{
// Raise this event after drawing
// the shape.
event EventHandler OnDraw;
}


// Base class event publisher inherits two
// interfaces, each with an OnDraw event
public class Shape : IDrawingObject, IShape
{
// Create an event for each interface event
event EventHandler PreDrawEvent;
event EventHandler PostDrawEvent;

// Explicit interface implementation required.
// Associate IDrawingObject's event with
// PreDrawEvent
event EventHandler IDrawingObject.OnDraw
{
add
{
lock (PreDrawEvent)
{
PreDrawEvent += value;
}
}
remove
{
lock (PreDrawEvent)
{
PreDrawEvent -= value;
}
}
}
// Explicit interface implementation required.
// Associate IShape's event with
// PostDrawEvent
event EventHandler IShape.OnDraw
{
add
{
lock (PostDrawEvent)
{
PostDrawEvent += value;
}
}
remove
{
lock (PostDrawEvent)
{
PostDrawEvent -= value;
}
}


}

// For the sake of simplicity this one method
// implements both interfaces.
public void Draw()
{
// Raise IDrawingObject's event before the object is drawn.
EventHandler handler = PreDrawEvent;
if (handler != null)
{
handler(this, new EventArgs());
}
Console.WriteLine("Drawing a shape.");

// RaiseIShape's event after the object is drawn.
handler = PostDrawEvent;
if (handler != null)
{
handler(this, new EventArgs());
}
}
}
public class Subscriber1
{
// References the shape object as an IDrawingObject
public Subscriber1(Shape shape)
{
IDrawingObject d = (IDrawingObject)shape;
d.OnDraw += new EventHandler(d_OnDraw);
}

void d_OnDraw(object sender, EventArgs e)
{
Console.WriteLine("Sub1 receives the IDrawingObject event.");
}
}
// References the shape object as an IShape
public class Subscriber2
{
public Subscriber2(Shape shape)
{
IShape d = (IShape)shape;
d.OnDraw += new EventHandler(d_OnDraw);
}

void d_OnDraw(object sender, EventArgs e)
{
Console.WriteLine("Sub2 receives the IShape event.");
}
}


public class Program
{
static void Main(string[] args)
{
Shape shape = new Shape();
Subscriber1 sub = new Subscriber1(shape);
Subscriber2 sub2 = new Subscriber2(shape);
shape.Draw();

Console.WriteLine("Press Enter to close this window.");
Console.ReadLine();
}
}

}