1. Trang chủ
  2. » Công Nghệ Thông Tin

C# 3.0 Cookbook phần 3 pps

88 488 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 88
Dung lượng 369,52 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Replacing the Stack and Queue with Their Generic Counterparts | 151 Console.WriteLinenumericQueue.Dequeue ; Console.WriteLinenumericQueue.Dequeue .ToString ; } Here is that same code u

Trang 1

Replacing the Stack and Queue with Their Generic Counterparts | 151

Console.WriteLine(numericQueue.Dequeue( ));

Console.WriteLine(numericQueue.Dequeue( ).ToString( ));

}

Here is that same code using aSystem.Collections.Generic.Queue object:

public static void UseGenericQueue( )

{

// Create a generic Queue object.

Queue<int> numericQueue = new Queue<int>( );

Here is a simple example of using aSystem.Collections.Stack object:

public static void UseNonGenericStack( )

{

// Create a non-generic Stack object.

Stack numericStack = new Stack( );

// Populate Stack (causing a boxing operation to occur).

Here is that same code using aSystem.Collections.Generic.Stack object:

public static void UseGenericStack( )

{

// Create a generic Stack object.

Stack<int> numericStack = new Stack<int>( );

Trang 2

a type argument in order to create the type The type argument in thisexample isan

int Thistype argument indicatesthat thisQueueorStackobject will be able to tain only integer types, aswell asany type that implicitly convertsto an integer, such

con-as ashort:

short s = 300;

numericQueue.Enqueue(s); // OK, because of the implicit conversion

However, a type that cannot be implicitly converted to an integer, such as adouble,will cause a compile-time error:

When choosing between a generic and nongenericQueueorStack, you need to decidewhether you wish to use a generic Queue or Stack object or a nongeneric Queue orStackobject Choosing the genericQueueorStackclass over its nongeneric form givesyou many benefits, including:

Type-safety

Each element contained in the data structure is typed to one specific type Thismeansno more casting of objectswhen they are added to or removed from thedata structure You cannot store multiple disparate types within a single datastructure; you always know what type is stored within the data structure Typechecking isdone at compile time rather than runtime Thisboilsdown to writ-ing less code, achieving better performance, and making fewer errors

Shortened development time

To make a type-safe data structure without using generics means having to class the System.Collections.Queue or System.Collections.Stack class in order

sub-to create your own This is time-consuming and error-prone

Performance

The genericQueueorStackdoesnot require a cast that could fail to occur whenadding and removing elementsfrom it In addition, no boxing operation occurs

Trang 3

Replacing the Stack and Queue with Their Generic Counterparts | 153

when adding a value type to theQueueorStack Likewise, in almost all cases, nounboxing operation occurs when removing a value type from theQueue or Stack.

Easier-to-read code

Your code base will be much smaller because you will not have to subclass thenongenericQueueorStackclass to create your own strongly typed class In addi-tion, the type-safety features of generic code will allow you to better understandwhat the purpose of theQueue or Stack class is in your code.

The following class members are implemented in the nongeneric Queue and Stackclasses but not in their generic counterparts:

One way around the missingClonemethod in the genericQueueandStackclasses is

to use the constructor that accepts anIEnumerable<T> type Since thisisone of theinterfacesthat the Queue and Stack classes implement, it is easy to write For theQueue object, the code is as follows:

public static void CloneQueue( )

{

// Create a generic Queue object.

Queue<int> numericQueue = new Queue<int>( );

// Populate Queue.

numericQueue.Enqueue(1);

numericQueue.Enqueue(2);

numericQueue.Enqueue(3);

// Create a clone of the numericQueue.

Queue<int> clonedNumericQueue = new Queue<int>(numericQueue);

// This does a simple peek at the values, not a dequeue.

foreach (int i in clonedNumericQueue)

Trang 4

For theStack object, the code is as follows:

public static void CloneStack( )

{

// Create a generic Stack object.

Stack<int> numericStack = new Stack<int>( );

// Populate Stack.

numericStack.Push(1);

numericStack.Push(2);

numericStack.Push(3);

// Clone the numericStack object.

Stack<int> clonedNumericStack = new Stack<int>(numericStack);

// This does a simple peek at the values, not a pop.

foreach (int i in clonedNumericStack)

The “System.Collections.Stack Class,” “System.Collections.Generic.Stack Class,”

“System.Collections.Queue Class,” and “System.Collections.Generic.Queue Class”topics in the MSDN documentation

Trang 5

Using a Linked List | 155

public static void UseLinkedList( )

new TodoItem( ) { Name = "hang door", Comment = "Should be done last" };

// Create a new LinkedList object

LinkedList<TodoItem> todoList = new LinkedList<TodoItem>( );

// Add the items

todoList.AddFirst(i1);

todoList.AddFirst(i2);

todoList.AddBefore(todoList.Find(i1), i3);

todoList.AddAfter(todoList.Find(i1), i4);

// Display all items

foreach (TodoItem tdi in todoList)

Trang 6

156 | Chapter 4: Generics

The output for this method is shown here:

buy door : Should be done first

assemble door : Should be done second

paint door : Should be done third

hang door : Should be done last

todoList.First.Value.Name == buy door

todoList.First.Next.Value.Name == assemble door

todoList.Last.Previous.Value.Name == paint door

ThisistheTodoItemclass, which is a simple container of two string propertiesNameandComment The propertiesuse the new Automatically Implemented Propertiesfea-ture in C# 3.0 that allowsyou to declare properties, and the definition of the back-ing fields is generated automatically:

Notice that each node (i.e., the square boxes) contains a reference to the next node(i.e., the arrowspointing to the right) and a pointer to the previousnode (i.e., thearrows pointing to the left) in the linked list In contrast, a singly linked list containsonly pointers to the next node in the list There is no pointer to the previous node

Figure 4-1 Graphical representation of a doubly linked list with three nodes

Trang 7

Using a Linked List | 157

In the LinkedList<T> class, the previous node is always accessed through thePreviousproperty, and the next node is always accessed through theNextproperty.The first node’sPreviousproperty in the linked list always returns anullvalue Like-wise, the last node’sNext property in the linked list always returns a null value.Each node (represented by the boxes in Figure 4-1) in the linked list is actually agenericLinkedListNode<T>object So aLinkedList<T>object isactually a collection ofLinkedListNode<T>objects Each of theseLinkedListNode<T>objectscontainsproper-tiesto accessthe next and previousLinkedListNode<T>objects, aswell asthe objectcontained within it The object contained in theLinkedListNode<T>object is accessedthrough the Value property In addition to these properties, a LinkedListNode<T>object also contains a property called List, which allowsaccessto the containing LinkedList<T> object.

Items to be aware of withList<T> and LinkedList<T>:

• Adding and removing nodeswithin aList<T>is, in general, faster than the sameoperation using aLinkedList<T> class.

• A List<T> stores its data essentially in one big array on the managed heap,whereastheLinkedList<T>can potentially store its nodes all over the managedheap Thisforcesthe garbage collector to work that much harder to manageLinkedList<T> node objects on the managed heap.

• Note that theList<T>.Insert* methodscan be slower than adding a node where within a LinkedList<T>using one of its Add* methods However, this isdependent on where the object isinserted into theList<T> An Insert methodmust shift all the elements within theList<T>object at the point where the newelement is inserted up by one position If the new element is inserted at or nearthe end of theList<T>, the overhead of shifting the existing elements is negligi-ble compared to the garbage collector overhead of managing theLinkedList<T>nodesobjects Another area where the List<T> can outperform theLinkedList<T>is when you’re doing an indexed access With the List<T>, youcan use the indexer to do an indexed lookup of the element at the specified posi-tion However, with aLinkedList<T>class, you do not have that luxury With aLinkedList<T>class, you must navigate theLinkedListNode<T>objectsusing thePrevious and Next propertieson eachLinkedListNode<T>, running through thelist until you find the one at the specified position

any-• AList<T>class also has performance benefits over a LinkedList<T>class whensearching for an element or node TheList<T>.BinarySearchmethod isfaster atfinding elementswithin aList<T>object than itscomparable methodswithin theLinkedList<T> class, namely the Contains, Find, and FindLast methods.

Table 4-2 shows the comparison betweenList<T> and LinkedList<T>.

Trang 8

158 | Chapter 4: Generics

See Also

The “LinkedList<T> Class” topic in the MSDN documentation

Null

Problem

You have a variable that isa numeric type, which will hold a numeric value obtainedfrom a database The database may return this value as anull You need a simple,clean way to store this numeric value, even if it is returned as anull.

Solution

Use a nullable value type There are two waysof creating a nullable value type The

first way is to use the ? type modifier:

int? myDBInt = null;

The second way is to use theNullable<T> generic type:

Nullable<int> myDBInt = new Nullable<int>( );

Discussion

Both of the following statements are equivalent:

int? myDBInt = null;

Nullable<int> myDBInt = new Nullable<int>( );

In both cases,myDBInt is a nullable type and is initialized to null.

A nullable type implementstheINullableValue interface, which hastwo read-onlyproperty members,HasValue and Value The HasValueproperty returns false if thenullable value isset tonull; otherwise, it returns true If HasValuereturnstrue, youcan access theValueproperty, which containsthe currently stored value IfHasValuereturns false and you attempt to read the Value property, you will get anInvalidOperationExceptionthrown Thisisbecause the Valueproperty isundefined

at this point Below is an example of a test of nullable value using theHasValueerty value:

prop-Table 4-2 Performance comparison between List<T> and LinkedList<T>

Adding/Removing Nodes List<T>

Inserting nodes LinkedList<T>*

Indexed access List<T>

Node searching List<T>

Trang 9

Creating a Value Type That Can Be Initialized to Null | 159

if (myDBInt.HasValue)

Console.WriteLine("Has a value: " + myDBInt.Value);

else

Console.WriteLine("Does not have a value (NULL)");

In addition, one can simply compare the value tonull, as shown below:

if (myDBInt != null)

Console.WriteLine("Has a value: " + myDBInt.Value);

else

Console.WriteLine("Does not have a value (NULL)");

Either method is acceptable

When casting a nullable value to a non-nullable value, the cast operates as it wouldnormally, except when the nullable type isset to null In thiscase, anInvalidOperationException isthrown When casting a non-nullable value to a nul-lable value, the cast operates as it would normally No InvalidOperationExceptionwill be thrown, as the non-nullable value can never benull.

The tricky thing to watch out for with nullable typesiswhen comparisonsare formed For example, if the following code is executed:

if (myTempDBInt < 100)

Console.WriteLine("myTempDBInt < 100");

else

Console.WriteLine("myTempDBInt >= 100");

The text “myTempDBInt >= 100” is displayed, which is obviously incorrect if the value

ofmyTempDBIntisnull To fix thiscode, you have to check if myTempDBIntisnull If it

is not, you can execute theif statement in the previous code block:

int? Result = DBInt + Value; // Result == 12

The result of using a nullable value in most operators is anullif any nullable value isnull.

Trang 10

Use LINQ to Objects to query theSortedList<T>and apply adescendingorder to theinformation in the list After instantiating a SortedList<TKey, TValue>, the key ofwhich isan intand the value of which isastring, a series of unordered numbersand their text representations are inserted into the list Those items are thendisplayed:

SortedList<int, string> data = new SortedList<int, string>( );

Trang 11

Reversing the Contents of a Sorted List | 161

Now the sort order is reversed by creating a query using LINQ to Objects and ting theorderbyclause todescending The results are then displayed from the queryresult set:

// query ordering by descending

var query = from d in data

orderby d.Key descending

// requery ordering by descending

query = from d in data

orderby d.Key descending

// Just go against the original list for ascending

foreach (KeyValuePair<int, string> kvp in data)

Trang 12

162 | Chapter 4: Generics

Discussion

ASortedListblends array and list syntax to allow for accessing the data in either mat, which can be a handy thing to do The data is accessible as key/value pairs ordirectly by index and will not allow duplicate keysto be added In addition, valuesthat are reference or nullable typescan benull, but keyscannot The itemscan beiterated using a foreach loop, with KeyValuePair being the type returned Whileaccessing elements of theSortedList<T>, they may only be read from The usual iter-ator syntax prohibits updating or deleting elements of the list while reading, as it willinvalidate the iterator

for-Theorderbyclause in the query causes the result set of the query to be ordered either

inascending(the default) ordescendingorder This sorting is accomplished throughuse of the default comparer for the element type, so it can be affected by overridingtheEqualsmethod for elements that are custom classes Multiple keys can be speci-fied for theorderbyclause, which has the effect of nesting the sort order such as sort-ing by “last name” and then “first name.”

can-public class Lottery

// pick the winning numbers

_numbers = new List<int>(5) { 17, 21, 32, 44, 58 };

}

Trang 13

Making Read-Only Collections the Generic Way | 163

public ReadOnlyCollection<int> Results

{

// return a wrapped copy of the results

get { return new ReadOnlyCollection<int>(_numbers); }

}

}

Lottery hasa List<int> of winning numbersthat it fillsin the constructor Theinteresting part is that it also exposes a property called Results, which returnsa ReadOnlyCollectiontyped as<int>for seeing the winning numbers Internally, a newReadOnlyCollectionwrapper iscreated to hold theList<int>that hasthe numbersin

it, and then this instance is returned for use by the user

If users then attempt to set a value on the collection, they get a compile error:

Lottery tryYourLuck = new Lottery( );

// Print out the results.

for (int i = 0; i < tryYourLuck.Results.Count; i++)

eas-See Also

The “ReadOnlyCollection” topic in the MSDN documentation

Trang 14

Here is a simple example of using aSystem.Collections.Hashtable object:

public static void UseNonGenericHashtable( )

{

Console.WriteLine("\r\nUseNonGenericHashtable");

// Create and populate a Hashtable

Hashtable numbers = new Hashtable( )

{ {1, "one"},"one"}, // Causes a boxing operation to occur for the key {2, "two"} }; // Causes a boxing operation to occur for the key

// Display all key/value pairs in the Hashtable

// Causes an unboxing operation to occur on each iteration for the key

foreach (DictionaryEntry de in numbers)

// Create and populate a Dictionary

Dictionary<int, string> numbers = new Dictionary<int, string>( )

{ { 1, "one" }, { 2, "two" } };

// Display all key/value pairs in the Dictionary

foreach (KeyValuePair<int, string> kvp in numbers)

{

Trang 15

Replacing the Hashtable with Its Generic Counterpart | 165

Console.WriteLine("Key: " + kvp.Key + "\tValue: " + kvp.Value);

Table 4-3 shows the equivalent members that are implemented in both classes

Table 4-3 Equivalent members in the Hashtable and the generic Dictionary classes

Members in the Hashtable class Equivalent members in the generic Dictionary class

Count property Count property

IsFixedSize property ((IDictionary)myDict).IsFixedSize

IsReadOnly property ((IDictionary)myDict).IsReadOnly

IsSynchronized property ((IDictionary)myDict).IsSynchronized

SyncRoot property ((IDictionary)myDict).SyncRoot

Values property Values property

Clear method Clear method

Clone method Use overloaded constructor, which accepts an IDictionary<T,U> type

Contains method ContainsKey method

ContainsKey method ContainsKey method

ContainsValue method ContainsValue method

CopyTo method ((ICollection)myDict).CopyTo(arr,0)

Remove method Remove method

Synchronized static method lock(myDictionary.SyncRoot) { }

Trang 16

166 | Chapter 4: Generics

In several cases within Table 4-3, there is not a one-to-one correlation between themembersof a Hashtableand the membersof the generic Dictionary class Startingwith the properties, notice that only theCount, Keys, Values, and Itempropertiesarepresent in both classes To make up for the missing properties in the Dictionaryclass, you can perform a cast to anIDictionary The following code shows how touse these casts to get at the missing properties:

Dictionary<int, string> numbers = new Dictionary<int, string>( );

Since theClonemethod is also missing from the genericDictionaryclass (due to thefact that thisclassdoesnot implement theICloneableinterface), you can instead usethe overloaded constructor, which accepts anIDictionary<T,U> type:

// Create and populate a Dictionary

Dictionary<int, string> numbers = new Dictionary<int, string>( )

{ { 1, "one" }, { 2, "two" } };

// Display all key/value pairs in the original Dictionary.

foreach (KeyValuePair<int, string> kvp in numbers)

{

Console.WriteLine("Original Key: " + kvp.Key + "\tValue: " + kvp.Value); }

// Clone the Dictionary object.

Dictionary<int, string> clonedNumbers = new Dictionary<int, string>(numbers);

// Display all key/value pairs in the cloned Dictionary.

foreach (KeyValuePair<int, string> kvp in numbers)

{

Console.WriteLine("Cloned Key: " + kvp.Key + "\tValue: " + kvp.Value); }

There are two more methods that are missing from theDictionaryclass, theContains

class In theHashtableclass, theContainsmethod and theContainsKeymethod bothexhibit the same behavior; therefore, you can simply use theContainsKeymethod oftheDictionary class to simulate the Contains method of the Hashtable class:

Trang 17

Replacing the Hashtable with Its Generic Counterpart | 167

// Create and populate a Dictionary

Dictionary<int, string> numbers =

new Dictionary<int, string>( )

// Create and populate a Dictionary

Dictionary<int, string> numbers =

new Dictionary<int, string>( )

{ { 1, "one" }, { 2, "two" } };

// Display all key/value pairs in the Dictionary.

foreach (KeyValuePair<int, string> kvp in numbers)

{

Console.WriteLine("Key: " + kvp.Key + "\tValue: " + kvp.Value);

}

// Create object array to hold copied information from Dictionary object.

KeyValuePair<int, string>[] objs = new KeyValuePair<int, string>[numbers.Count];

// Calling CopyTo on a Dictionary

// Copies all KeyValuePair objects in Dictionary object to objs[]

((IDictionary)numbers).CopyTo(objs, 0);

// Display all key/value pairs in the objs[].

foreach (KeyValuePair<int, string> kvp in objs)

See Also

The “System.Collections.Hashtable Class” and nary Class” topics in the MSDN documentation

Trang 18

// Create a Dictionary object and populate it

Dictionary<int, string> myStringDict = new Dictionary<int, string>( )

{ { 1, "Foo" }, { 2, "Bar" }, { 3, "Baz" } };

// Enumerate and display all key and value pairs.

foreach (KeyValuePair<int, string> kvp in myStringDict)

The nongeneric System.Collections.Hashtable (the counterpart to the System.

System.Collections.SortedList classes support foreach using the DictionaryEntrytype, as shown here:

Hashtable myHashtable = new Hashtable( )

{ { 1, "Foo" }, { 2, "Bar" }, { 3, "Baz" } };

foreach (DictionaryEntry de in myHashtable)

However, the Dictionaryobject supports the KeyValuePair<T,U>type when using a

IEnumerator, which in turn returns KeyValuePair<T,U> types, not DictionaryEntrytypes

TheKeyValuePair<T,U>type is well suited to be used when enumerating the genericDictionaryclass with aforeach loop TheDictionaryEntry object containskey andvalue pairsasobjects, whereasthe KeyValuePair<T,U> type containskey and valuepairsastheir original types, defined when creating theDictionaryobject Thisboosts

Trang 19

Constraining Type Arguments | 169

performance and can reduce the amount of code you have to write, asyou do nothave to cast the key and value pairs to their original types

public class DisposableList<T> : IList<T>

where T : class, IDisposable

{

private List<T> _items = new List<T>( );

// Private method that will dispose of items in the list

private void Delete(T item)

get {return (_items[index]);}

set {_items[index] = value;}

}

public void RemoveAt(int index)

{

Trang 21

Constraining Type Arguments | 171

on that object Thisallowsyou to transparently handle the management of anyobject stored within thisDisposableList object.

The following code exercises aDisposableList object:

public static void TestDisposableListCls( )

{

DisposableList<StreamReader> dl = new DisposableList<StreamReader>( );

// Create a few test objects.

StreamReader tr1 = new StreamReader("c:\\boot.ini");

StreamReader tr2 = new StreamReader("c:\\autoexec.bat");

StreamReader tr3 = new StreamReader("c:\\config.sys");

// Add the test object to the DisposableList.

// Call Dispose before any of the disposable objects are

// removed from the DisposableList.

dl.RemoveAt(0);

dl.Remove(tr1);

dl.Clear( );

}

Trang 22

172 | Chapter 4: Generics

Discussion

Thewhere keyword is used to constrain a type parameter to accept only argumentsthat satisfy the given constraint For example, theDisposableListhasthe constraintthat any type argumentT must implement the IDisposable interface:

public class DisposableList<T> : IList<T>

where T : IDisposable

This means that the following code will compile successfully:

DisposableList<StreamReader> dl = new DisposableList<StreamReader>( );

but the following code will not:

DisposableList<string> dl = new DisposableList<string>( );

Thisisbecause thestring type doesnot implement the IDisposableinterface, andtheStreamReader type does.

Other constraints on the type argument are allowed, in addition to requiring one ormore specific interfaces to be implemented You can force a type argument to beinherited from a specific base class, such as theTextReader class:

public class DisposableList<T> : IList<T>

where T : System.IO.TextReader, IDisposable

You can also determine if the type argument is narrowed down to only value types oronly reference types The following class declaration is constrained to using onlyvalue types:

public class DisposableList<T> : IList<T>

where T : struct

This class declaration is constrained to only reference types:

public class DisposableList<T> : IList<T>

where T : class

In addition, you can also require any type argument to implement a public defaultconstructor:

public class DisposableList<T> : IList<T>

where T : IDisposable, new( )

Using constraints allows you to write generic types that accept a narrower set ofavailable type arguments If theIDisposableconstraint is omitted in the Solution forthisrecipe, a compile-time error will occur Thisisbecause not all of the typesthatcan be used as the type argument for the DisposableList class will implement theIDisposableinterface If you skip this compile-time check, a DisposableList objectmay contain objectsthat do not have a public no-argumentDisposemethod In thiscase, a runtime exception will occur Generics and constraints in particular forcestrict type checking of the class-type arguments and allow you to catch these prob-lems at compile time rather than at runtime

Trang 23

Initializing Generic Variables to Their Default Values | 173

See Also

The “where Keyword” topic in the MSDN documentation

4.12 Initializing Generic Variables to Their Default

Values

Problem

You have a generic class that contains a variable of the same type as the type ter defined by the class itself Upon construction of your generic object, you wantthat variable to be initialized to its default value

parame-Solution

Simply use thedefault keyword to initialize that variable to its default value:

public class DefaultValueExample<T>

The code to use this class is shown here:

public static void ShowSettingFieldsToDefaults( )

{

DefaultValueExample<int> dv = new DefaultValueExample<int>( );

// Check if the data is set to its default value; true is returned.

bool isDefault = dv.IsDefaultData( );

Console.WriteLine("Initial data: " + isDefault);

Trang 24

Initial data: True

Set data: False

Thedefaultkeyword allowsyou to tell the compiler that at compile time the defaultvalue of this variable should be used If the type argument supplied is a numericvalue (e.g.,int, long, decimal), then the default value iszero If the type argumentsupplied is a reference type, then the default value isnull If the type argument sup-plied isa struct, then the default value of the struct isdetermined by initializingeach member field to its default value

See Also

Recipe 4.6, and the “default Keyword in Generic Code” topic in the MSDNdocumentation

Trang 25

pairs The following collection types consist of a straightforward list of elements: System.Collections.ArrayList

These collection classes are organized under theSystem.Collectionsand theSystem Collections.Generic namespaces In addition to these namespaces, another name-space calledSystem.Collections.Specializedcontainsa few more useful collectionclasses These classes might not be as well known as the previous classes, so here is ashort explanation of the collection classes under theSystem.Collections.Specializednamespace:

Trang 26

This class contains two static methods: one to create a case-insensitiveHashtableand another to create a case-insensitiveSortedList When you directly create a HashtableandSortedListobject, you always create a case-sensitiveHashtableorSortedList, unless you use one of the constructors that take an IComparerandpassCaseInsensitiveComparer.Default to it.

NameValueCollection

This collection consists of key and value pairs, which are both of typeString.The interesting thing about this collection is that it can store multiple stringvalues with a single key The multiple string values are comma-delimited TheString.Split method is useful when breaking up multiple strings in a value StringCollection

This collection is a simple list containing string elements This list acceptsnullelements as well as duplicate strings This list is case-sensitive

StringDictionary

ThisisaHashtablethat stores both the key and value as strings Keys are verted to all-lowercase letters before being added to theHashtable, allowing forcase-insensitive comparisons Keys cannot benull, but values may be set to null.The C# compiler also supports a fixed-size array Arrays of any type may be createdusing the following syntax:

int[] foo = new int[2];

Trang 27

Swapping Two Elements in an Array | 177

int[,] foo = new int[2,3]; // A 2-dimensional array

Jagged arrays are arrays of arrays If you picture a jagged array as a one-dimensionalarray with each element in that array containing another one-dimensional array, itcould have a different number of elementsin each row A jagged array isdefined asfollows:

int[][] baz = new int[2][] {new int[2], new int[3]};

Thebazarray consists of a one-dimensional array containing two elements Each ofthese elements consists of another array, the first array having two elements and thesecond array having three

The rest of this chapter contains recipes dealing with arrays and the various tion types

Trang 28

This code produces the following output:

Element 0 = 1 ← The original array

You can use the staticReverse method, as in this snippet of code:

int[] someArray = new int[5] {1,2,3,4,5};

Trang 29

Reversing an Array Quickly | 179

theArray[counter] = theArray[theArray.Length - counter - 1];

theArray[theArray.Length - counter - 1] = tempHolder;

Discussion

The followingTestArrayReversalmethod creates a test array of five integers and playsthe elementsin their initial order Next, theDoReversal<T>method iscalled toreverse the elements in the array After this method returns, the array is then dis-played a second time as a reversed array:

public static void TestArrayReversal( )

{

int[] someArray = new int[5] {1,2,3,4,5};

for (int counter = 0; counter < someArray.Length; counter++)

Trang 30

180 | Chapter 5: Collections

This code displays the following:

Element 0 = 1 ← The original array

Note that thisisinteger division, so if the array length is an odd number, the

remain-der is discarded Since your array length is five, thefor loop counts from zero to one.Inside of the loop are three lines of code:

tempHolder = theArray[counter];

theArray[counter] = theArray[theArray.Length - counter - 1];

theArray[theArray.Length - counter - 1] = tempHolder;

These three lines swap the first half of the array with the second half As theforloopcounts from zero, these three lines swap the first and last elements in the array Theloop incrementsthe counter by one, allowing the second element and the next-to-last element to be swapped This continues until all elements in the array have beenswapped

There isone element in the array that cannot be swapped; thisisthe middle element

of an array with an odd number for the length For example, in thiscode, there arefive elements in the array The third element should not be swapped Put anotherway, all of the other elementspivot on thisthird element when they are swapped.This does not occur when the length of the array is an even number

By dividing the array length by two, you can compensate for even or odd array ments Since you get back an integer number from this division, you can easily skipover the middle element in an array with an odd length

ele-See Also

Recipe 5.1, and the “Array.Reverse Method” topic in the MSDN documentation

Trang 31

Writing a More Flexible StackTrace Class | 181

be much better if theStackTrace object operated like a collection.

The StackTrace object can now be used as if it were a collection of StackFrameobjects To obtain aStackTraceobject for the current point in code, use the follow-ing code:

StackTrace sTrace = new StackTrace( );

IList<StackFrame> frames = sTrace.ToList( );

To display a portion or all of the stack trace, use the following code:

// Display the first stack frame.

Console.WriteLine(frames[0].ToString( ));

Example 5-1 Writing a More Flexible StackTrace Class

public static ReadOnlyCollection<StackFrame> ToList(this StackTrace stackTrace)

var frames = new StackFrame[stackTrace.FrameCount];

for (int counter = 0; counter < stackTrace.FrameCount; counter++)

Trang 32

182 | Chapter 5: Collections

// Display all stack frames.

foreach (StackFrame SF in frames)

To copy theStackFrame objects to a new array, use the following code:

StackFrame[] myNewArray = new StackFrame[frames.Count];

isusu-to be discarded:

StackTraceList arrStackTrace = new StackTraceList(1);

See Also

The “StackTraceClass” and “ReadOnlyCollection class” topics in the MSDN

docu-mentation Also see the “Adapter Design Pattern” chapter in Design Patterns by

Gamma et al (Addison-Wesley)

Appears in a List<T>

Problem

You need the number of occurrencesof one type of object contained in a List<T>.TheList<T> contains methods, such asContains andBinarySearch, to find a single

Trang 33

Determining the Number of Times an Item Appears in a List<T> | 183

item Unfortunately, these methods cannot find all duplicated items at one time—

essentially, there is no count all functionality If you want to find multiple items, you

need to implement your own routine

Solution

Use the two methods defined in Example 5-2—CountAllandBinarySearchCountAll.These methods extend theList<T>class to return the number of times a particularobject appears in a sorted and an unsortedList<T>.

Discussion

The CountAll method acceptsa search value (searchValue) of generic type T Thismethod then proceedsto count the number of timesthe search value appearsin theList<T>class This method may be used when the List<T>is sorted or unsorted Ifthe List<T> issorted (a List<T> issorted by calling the Sort method), the

Example 5-2 Determining the number of times an item appears in a List <T>

using System;

using System.Collections;

using System.Collections.Generic;

using System.Linq;

static class CollectionExtMethods{

// Count the number of times an item appears in this

// unsorted or sorted List<T>

public static int CountAll<T>(this List<T> myList, T searchValue)

{

return ((from t in myList where t.Equals(searchValue) select t).Count( ));

}

// Count the number of times an item appears in this sorted List<T>.

public static int BinarySearchCountAll<T>(this List<T> myList, T searchValue)

{

// Search for first item.

int center = myList.BinarySearch(searchValue);

int left = center;

while (left < 0 && myList[left-1].Equals(searchValue))

{

left -= 1;

}

int right = center;

while (right < (myList.Count – 1) && myList[right+1].Equals(searchValue))

Trang 34

184 | Chapter 5: Collections

BinarySearchCountAllmethod can be used to increase the efficiency of the searching.Thisisdone by making use of theBinarySearchmethod on theList<T>class, whichismuch faster than iterating through the entireList<T> Thisisespecially true asthe List<T> grows in size.

The following code exercises these two new methods of theList<T> class:

Trang 35

Retrieving All Instances of a Specific Item in a List<T> | 185

(foundCounter) isincremented by one Thiscounter isreturned by thismethod toindicate the number of items matching the search criteria in theList<T>.

matching the search criteria (searchValue) in the List<T> If one isfound, a whileloop is used to find the very first matching item in the sortedList<T>, and the posi-tion of that element isrecorded in theleftvariable A secondwhile loop isused tofind the very last matching item, and the position of this element is recorded in theright variable The value in the left variable issubtracted from the value in therightvariable, and then one isadded to thisresult in order to get the total number ofmatches

Recipe 5.5 containsa variation of thisrecipe that returnsthe actual itemsfoundrather than a count

See Also

Recipe 5.5, and the “List<T> Class” topic in the MSDN documentation

List<T>

Problem

You need to retrieve every object contained in aList<T>that matchesa search rion The List<T> containstheBinarySearch method to find a single item—essen-

crite-tially, there isno find all functionality If you want to find all itemsduplicated in a

List<T>, you must write your own routine.

Solution

Use the GetAll and BinarySearchGetAll methodsshown in Example 5-3, whichextend theList<T>class These methods return an array of all the matching objectsfound in a sorted or unsortedList<T>.

Example 5-3 Retrieving all instances of a specific item in a List<T>

// The method to retrieve all matching objects in a

// sorted or unsorted ListEx<T>

public T[] GetAll(T searchValue)

{

Trang 36

186 | Chapter 5: Collections

Discussion

TheGetAll and BinarySearchGetAllmethods used in this recipe are very similar tothose used in Recipe 5.4 The main difference is that these methods return the actualitemsfound in aList<T> object instead of a count of the number of times an itemwas found The main thing to keep in mind when choosing between these methods

List<T> foundItem = new List<T>( );

for (int index = 0; index < this.Count; index++)

// The method to retrieve all matching objects in a sorted ListEx<T>

public static T[] BinarySearchGetAll<T>(this List<T> myList, T searchValue)

{

List<T> RetObjs = new List<T>( );

// Search for first item.

int center = myList.BinarySearch(searchValue);

if (center > 0)

{

RetObjs.Add(myList[center]);

int left = center;

while (left > 0 && myList[left - 1].Equals(searchValue))

{

left -= 1;

RetObjs.Add(myList[left]);

}

int right = center;

while (right < (myList.Count - 1) &&

Trang 37

Retrieving All Instances of a Specific Item in a List<T> | 187

iswhether you are going to be searching aList<T>that is sorted or unsorted Choosethe GetAllmethod to obtain an array of all found itemsfrom an unsorted List<T>and choose theBinarySearchGetAll method to get all items in a sorted List<T>.The following code exercises these two new extension methods of theList<T> class: class Test

IEnumerable<int> objects = arrayExt.GetAll(2);

foreach (object o in objects)

int[] objs = arrayExt.BinarySearchGetAll(-2);

foreach (object o in objs)

Trang 38

See Also

Recipe 5.4, and the “List<T> Class” topic in the MSDN documentation

Problem

You need the ability to insert and remove items from a standardSystem.Arraytype.When an item is inserted, it should not overwrite the item where it is being inserted;instead, it should be inserted between the element at that index and the previousindex When an item is removed, the void left by the element should be closed byshifting the other elements in the array However, the Array type hasno usablemethod to perform these operations

Solution

If possible, switch to aList<T>instead If this is not possible (for example, if you’renot in control of the code that createstheArrayorArrayListin the first place), usethe approach shown in the following class Two methods insert and remove itemsfrom the array TheInsertIntoArray method that extendstheArray type will insert

an item into the array without overwriting any data that already exists in the array

Trang 39

Inserting and Removing Items from an Array | 189

from the array:

using System;

public static class ArrayUtilities

{

public static void InsertIntoArray(this Array target,

object value, int index)

{

if (index < target.GetLowerBound(0) ||

index > target.GetUpperBound(0))

{

throw (new ArgumentOutOfRangeException("index", index,

"Array index out of bounds."));

throw (new ArgumentOutOfRangeException("index", index,

"Array index out of bounds."));

static Both methods make use of theArray.Copystatic method to perform their ations Initially, both methods test to see whether an item is being added or removedwithin the boundsof the array target If the item passes this test, the Array.Copymethod is used to shift items around to either make room for an element to beinserted or to overwrite an element being removed from the array

Trang 40

oper-190 | Chapter 5: Collections

TheRemoveFromArraymethod accepts two parameters The first array,target, isthearray from which an element isto be removed; the second parameter,index, isthezero-based position of the element to be removed in the array Elements at and abovethe inserted element are shifted down by one The last element in the array is set tothe default value for the array type

TheInsertIntoArraymethod accepts three parameters The first parameter,target,isthe array that isto have an element added;valueisthe element to be added; and

indexis the zero-based position at whichvalueisto be added Elementsat and abovethe inserted element are shifted up by one The last element in the array is discarded.The following code illustrates the use of the InsertIntoArray and RemoveFromArraymethods:

Ngày đăng: 12/08/2014, 09:22

TỪ KHÓA LIÊN QUAN