Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Visual CSharp 2005 Recipes (2006) [eng]

.pdf
Скачиваний:
48
Добавлен:
16.08.2013
Размер:
4.04 Mб
Скачать

28 C H A P T E R 1 A P P L I C AT I O N D E V E L O P M E N T

Solution

Build server-based solutions where possible so that people don’t have access to your assemblies. If you must distribute assemblies, you have no way to stop people from decompiling them. The best you can do is use obfuscation and components compiled to native code to make your assemblies more difficult to decompile.

How It Works

Because .NET assemblies consist of a standardized, platform-independent set of instruction codes and metadata that describes the types contained in the assembly, they are relatively easy to decompile. This allows decompilers to generate source code that is close to your original code with ease, which can be problematic if your code contains proprietary information or algorithms that you want to keep secret.

The only way to ensure people can’t decompile your assemblies is to stop people from getting your assemblies in the first place. Where possible, implement server-based solutions such as Microsoft ASP.NET applications and Web services. With the security correctly configured on your server, nobody will be able to access your assemblies and therefore won’t be able to decompile them.

When building a server solution is not appropriate, you have the following two options:

Use an obfuscator to make it difficult to understand your code once it is decompiled. Some versions of Visual Studio include the Community Edition of an obfuscator named Dotfuscator. Obfuscators use a variety of techniques to make your assembly difficult to decompile; principal among these techniques are

Renaming of private methods and fields in such a way that it’s difficult to read and understand the purpose of your code, and

Inserting control flow statements to make the logic of your application difficult to follow.

Build the parts of your application that you want to keep secret in native DLLs or COM objects, and then call them from your managed application using P/Invoke or COM Interop. (See Chapter 12 for recipes that show you how to call unmanaged code.)

Neither approach will stop a skilled and determined person from reverse engineering your code, but both approaches will make the job significantly more difficult and deter most casual observers.

Note The risks of application decompilation aren’t specific to C# or .NET. A determined person can reverse engineer any software if they have the time and the skill.

1-16. Manipulate the Appearance of the Console

Problem

You want to control the visual appearance of the Windows console.

Solution

Use the static properties and methods of the System.Console class.

C H A P T E R 1 A P P L I C AT I O N D E V E L O P M E N T

29

How It Works

Version 2.0 of the .NET Framework dramatically enhances the control you have over the appearance and operation of the Windows console. Table 1-3 describes the properties and methods of the Console class that you can use to control the console’s appearance.

Table 1-3. Properties and Methods to Control the Appearance of the Console

Member

Description

Properties

 

BackgroundColor

Gets and sets the background color of the console using one of the values

 

from the System.ConsoleColor enumeration. Only new text written to the

 

console will appear in this color. To make the entire console this color,

 

call the method Clear after you have configured the BackgroundColor

 

property.

BufferHeight

Gets and sets the buffer height in terms of rows.

BufferWidth

Gets and sets the buffer width in terms of columns.

CursorLeft

Gets and sets the column position of the cursor within the buffer.

CursorSize

Gets and sets the height of the cursor as a percentage of a character cell.

CursorTop

Gets and sets the row position of the cursor within the buffer.

CursorVisible

Gets and sets whether the cursor is visible.

ForegroundColor

Gets and sets the text color of the console using one of the values from

 

the System.ConsoleColor enumeration. Only new text written to the

 

console will appear in this color. To make the entire console this color,

 

call the method Clear after you have configured the ForegroundColor

 

property.

LargestWindowHeight

Returns the largest possible number of rows based on the current font

 

and screen resolution.

LargestWindowWidth

Returns the largest possible number of columns based on the current

 

font and screen resolution.

Title

Gets and sets text shown in the title bar.

WindowHeight

Gets and sets the width in terms of character rows.

WindowWidth

Gets and sets the width in terms of character columns.

Methods

 

Clear

Clears the console.

ResetColor

Sets the foreground and background colors to their default values as

 

configured within Windows.

SetWindowSize

Sets the width and height in terms of columns and rows.

 

 

The Code

The following example demonstrates how to use the properties and methods of the Console class to dynamically change the appearance of the Windows console:

using System;

namespace Apress.VisualCSharpRecipes.Chapter01

{

public class Recipe01_16

{

static void Main(string[] args)

30 C H A P T E R 1 A P P L I C AT I O N D E V E L O P M E N T

{

//Display the standard console. Console.Title = "Standard Console";

Console.WriteLine("Press Enter to change the console's appearance."); Console.ReadLine();

//Change the console appearance and redisplay.

Console.Title = "Colored Text"; Console.ForegroundColor = ConsoleColor.Red; Console.BackgroundColor = ConsoleColor.Green;

Console.WriteLine("Press Enter to change the console's appearance."); Console.ReadLine();

//Change the console appearance and redisplay. Console.Title = "Cleared / Colored Console"; Console.ForegroundColor = ConsoleColor.Blue; Console.BackgroundColor = ConsoleColor.Yellow; Console.Clear();

Console.WriteLine("Press Enter to change the console's appearance."); Console.ReadLine();

//Change the console appearance and redisplay.

Console.Title = "Resized Console"; Console.ResetColor(); Console.Clear(); Console.SetWindowSize(100, 50); Console.BufferHeight = 500; Console.BufferWidth = 100; Console.CursorLeft = 20; Console.CursorSize = 50; Console.CursorTop = 20; Console.CursorVisible = false;

Console.WriteLine("Main method complete. Press Enter."); Console.ReadLine();

}

}

}

C H A P T E R 2

■ ■ ■

Data Manipulation

Most applications need to manipulate some form of data. The Microsoft .NET Framework provides many techniques that simplify or improve the efficiency of common data-manipulation tasks. The recipes in this chapter describe how to do the following:

Manipulate the contents of strings efficiently to avoid the overhead of automatic string creation due to the immutability of strings (recipe 2-1)

Represent basic data types using different encoding schemes or as byte arrays to allow you to share data with external systems (recipes 2-2, 2-3, and 2-4)

Validate user input and manipulate string values using regular expressions (recipes 2-5 and 2-6)

Create System.DateTime objects from string values, such as those that a user might enter, and display DateTime objects as formatted strings (recipe 2-7)

Mathematically manipulate DateTime objects in order to compare dates or add/subtract periods of time from a date (recipe 2-8)

Sort the contents of an array or an ArrayList collection (recipe 2-9)

Copy the contents of a collection to an array (recipe 2-10)

Use the standard generic collection classes to instantiate a strongly typed collection (recipe 2-11)

Use generics to define your own general-purpose container or collection class that will be strongly typed when it is used (recipe 2-12)

Serialize object state and persist it to a file (recipe 2-13)

Read user input from the Windows console (recipe 2-14)

2-1. Manipulate the Contents of a String Efficiently

Problem

You need to manipulate the contents of a String object and want to avoid the overhead of automatic String creation caused by the immutability of String objects.

Solution

Use the System.Text.StringBuilder class to perform the manipulations and convert the result to a String object using the StringBuilder.ToString method.

31

32 C H A P T E R 2 D ATA M A N I P U L AT I O N

How It Works

String objects in .NET are immutable, meaning that once created their content cannot be changed. For example, if you build a string by concatenating a number of characters or smaller strings, the common language runtime (CLR) will create a completely new String object whenever you add a new element to the end of the existing string. This can result in significant overhead if your application performs frequent string manipulation.

The StringBuilder class offers a solution by providing a character buffer and allowing you to manipulate its contents without the runtime creating a new object as a result of every change. You can create a new StringBuilder object that is empty or initialized with the content of an existing String object. You can manipulate the content of the StringBuilder object using overloaded methods that allow you to insert and append string representations of different data types. At any time, you can obtain a String representation of the current content of the StringBuilder object by calling

StringBuilder.ToString.

Two important properties of StringBuilder control its behavior as you append new data: Capacity and Length. Capacity represents the size of the StringBuilder buffer, and Length represents the length of the buffer’s current content. If you append new data that results in the number of characters in the StringBuilder object (Length) exceeding the capacity of the StringBuilder object (Capacity), the StringBuilder must allocate a new buffer to hold the data. The size of this new buffer is double the size of the previous Capacity value. Used carelessly, this buffer reallocation can negate much of the benefit of using StringBuilder. If you know the length of data you need to work with, or know an upper limit, you can avoid unnecessary buffer reallocation by specifying the capacity at creation time or setting the Capacity property manually. Note that 16 is the default Capacity property setting. When setting the Capacity and Length properties, be aware of the following behavior:

If you set Capacity to a value less than the value of Length, the Capacity property throws the exception System.ArgumentOutOfRangeException. The same exception is also thrown if you try to raise the Capacity setting above the value of the MaxCapacity property. This should not be a problem except if you want to allocate more that 2 gigabytes (GB).

If you set Length to a value less than the length of the current content, the content is truncated.

If you set Length to a value greater than the length of the current content, the buffer is padded with spaces to the specified length. Setting Length to a value greater than Capacity automatically adjusts the Capacity value to be the same as the new Length value.

The Code

The ReverseString method shown in the following example demonstrates the use of the StringBuilder class to reverse a string. If you did not use the StringBuilder class to perform this operation, it would be significantly more expensive in terms of resource utilization, especially as the input string is made longer. The method creates a StringBuilder object of the correct capacity to ensure that no buffer reallocation is required during the reversal operation.

using System; using System.Text;

namespace Apress.VisualCSharpRecipes.Chapter02

{

class Recipe02_01

{

public static string ReverseString(string str)

{

// Make sure we have a reversible string. if (str == null || str.Length <= 1)

C H A P T E R 2 D ATA M A N I P U L AT I O N

33

{

return str;

}

//Create a StringBuilder object with the required capacity. StringBuilder revStr = new StringBuilder(str.Length);

//Loop backward through the source string one character at a time and

//append each character to the StringBuilder.

for (int count = str.Length - 1; count > -1; count--)

{

revStr.Append(str[count]);

}

// Return the reversed string. return revStr.ToString();

}

public static void Main()

{

Console.WriteLine(ReverseString("Madam Im Adam"));

Console.WriteLine(ReverseString(

"The quick brown fox jumped over the lazy dog."));

// Wait to continue.

Console.WriteLine("\nMain method complete. Press Enter"); Console.ReadLine();

}

}

}

2-2. Encode a String Using Alternate Character

Encoding

Problem

You need to exchange character data with systems that use character-encoding schemes other than UTF-16, which is the character-encoding scheme used internally by the CLR.

Solution

Use the System.Text.Encoding class and its subclasses to convert characters between different encoding schemes.

How It Works

Unicode is not the only character-encoding scheme, nor is UTF-16 the only way to represent Unicode characters. When your application needs to exchange character data with external systems (particularly legacy systems) through an array of bytes, you may need to convert character data between UTF-16 and the encoding scheme supported by the other system.

34 C H A P T E R 2 D ATA M A N I P U L AT I O N

The abstract class Encoding and its concrete subclasses provide the functionality to convert characters to and from a variety of encoding schemes. Each subclass instance supports the conversion of characters between UTF-16 and one other encoding scheme. You obtain instances of the encoding-specific classes using the static factory method Encoding.GetEncoding, which accepts either the name or the code page number of the required encoding scheme.

Table 2-1 lists some commonly used character-encoding schemes and the code page number you must pass to the GetEncoding method to create an instance of the appropriate encoding class. The table also shows static properties of the Encoding class that provide shortcuts for obtaining the most commonly used types of encoding objects.

Table 2-1. Character-Encoding Classes

Encoding Scheme

Class

Create Using

ASCII

ASCIIEncoding

GetEncoding(20127) or the ASCII property

Default (current Microsoft

Encoding

GetEncoding(0) or the Default property

Windows default)

 

 

UTF-7

UTF7Encoding

GetEncoding(65000) or the UTF7 property

UTF-8

UTF8Encoding

GetEncoding(65001) or the UTF8 property

UTF-16 (Big Endian)

UnicodeEncoding

GetEncoding(1201) or the BigEndianUnicode

 

 

property

UTF-16 (Little Endian)

UnicodeEncoding

GetEncoding(1200) or the Unicode property

Windows OS

Encoding

GetEncoding(1252)

 

 

 

Once you have an Encoding object of the appropriate type, you convert a UTF-16 encoded Unicode string to a byte array of encoded characters using the GetBytes method. Conversely, you convert a byte array of encoded characters to a string using the GetString method.

The Code

The following example demonstrates the use of some encoding classes.

using System; using System.IO; using System.Text;

namespace Apress.VisualCSharpRecipes.Chapter02

{

class Recipe02_02

{

public static void Main()

{

// Create a file to hold the output.

using (StreamWriter output = new StreamWriter("output.txt"))

{

//Create and write a string containing the symbol for pi. string srcString = "Area = \u03A0r^2"; output.WriteLine("Source Text : " + srcString);

//Write the UTF-16 encoded bytes of the source string. byte[] utf16String = Encoding.Unicode.GetBytes(srcString); output.WriteLine("UTF-16 Bytes: {0}",

BitConverter.ToString(utf16String));

C H A P T E R 2 D ATA M A N I P U L AT I O N

35

//Convert the UTF-16 encoded source string to UTF-8 and ASCII. byte[] utf8String = Encoding.UTF8.GetBytes(srcString);

byte[] asciiString = Encoding.ASCII.GetBytes(srcString);

//Write the UTF-8 and ASCII encoded byte arrays. output.WriteLine("UTF-8 Bytes: {0}",

BitConverter.ToString(utf8String)); output.WriteLine("ASCII Bytes: {0}",

BitConverter.ToString(asciiString));

//Convert UTF-8 and ASCII encoded bytes back to UTF-16 encoded

//string and write.

output.WriteLine("UTF-8 Text : {0}", Encoding.UTF8.GetString(utf8String));

output.WriteLine("ASCII Text : {0}", Encoding.ASCII.GetString(asciiString));

}

// Wait to continue.

Console.WriteLine("\nMain method complete. Press Enter"); Console.ReadLine();

}

}

}

Usage

Running the code will generate a file named output.txt. If you open this file in a text editor that supports Unicode, you will see the following content:

Source Text : Area = πr^2

UTF-16 Bytes: 41-00-72-00-65-00-61-00-20-00-3D-00-20-00-A0-03-72-00-5E-00-32-00 UTF-8 Bytes: 41-72-65-61-20-3D-20-CE-A0-72-5E-32

ASCII Bytes: 41-72-65-61-20-3D-20-3F-72-5E-32 UTF-8 Text : Area = πr^2

ASCII Text : Area = ?r^2

Notice that using UTF-16 encoding, each character occupies 2 bytes, but because most of the characters are standard characters, the high-order byte is 0. (The use of little-endian byte ordering means that the low-order byte appears first.) This means that most of the characters are encoded using the same numeric values across all three encoding schemes. However, the numeric value for the symbol pi (emphasized in bold in the preceding output) is different in each of the encodings. The value of pi requires more than 1 byte to represent. UTF-8 encoding uses 2 bytes, but ASCII has no direct equivalent and so replaces pi with the code 3F. As you can see in the ASCII text version of the string, 3F is the symbol for an English question mark (?).

Caution If you convert Unicode characters to ASCII or a specific code page encoding scheme, you risk losing data. Any Unicode character with a character code that cannot be represented in the scheme will be ignored.

Notes

The Encoding class also provides the static method Convert to simplify the conversion of a byte array from one encoding scheme to another without the need to manually perform an interim

32d088203d70df39442d18a2c1065d0c

36C H A P T E R 2 D ATA M A N I P U L AT I O N

conversion to UTF-16. For example, the following statement converts the ASCII-encoded bytes contained in the asciiString byte array directly from ASCII encoding to UTF-8 encoding:

byte[] utf8String = Encoding.Convert(Encoding.ASCII, Encoding.UTF8,asciiString);

2-3. Convert Basic Value Types to Byte Arrays

Problem

You need to convert basic value types to byte arrays.

Solution

The static methods of the System.BitConverter class provide a convenient mechanism for converting most basic value types to and from byte arrays. An exception is the decimal type. To convert a decimal type to or from a byte array, you need to use a System.IO.MemoryStream object.

How It Works

The static method GetBytes of the BitConverter class provides overloads that take most of the standard value types and return the value encoded as an array of bytes. Support is provided for the bool, char, double, short, int, long, float, ushort, uint, and ulong data types. BitConverter also provides a set of static methods that support the conversion of byte arrays to each of the standard value types. These are named ToBoolean, ToUInt32, ToDouble, and so on.

Unfortunately, the BitConverter class does not provide support for converting the decimal type. Instead, write the decimal type to a MemoryStream instance using a System.IO.BinaryWriter object, and then call the MemoryStream.ToArray method. To create a decimal type from a byte array, create a MemoryStream object from the byte array and read the decimal type from the MemoryStream object using a System.IO.BinaryReader instance.

The Code

The following example demonstrates the use of BitConverter to convert a bool type and an int type to and from a byte array. The second argument to each of the ToBoolean and ToInt32 methods is a zero-based offset into the byte array where the BitConverter should start taking the bytes to create the data value. The code also shows how to convert a decimal type to a byte array using a MemoryStream object and a BinaryWriter object, as well as how to convert a byte array to a decimal type using

a BinaryReader object to read from the MemoryStream object.

using System; using System.IO;

namespace Apress.VisualCSharpRecipes.Chapter02

{

class Recipe02_03

{

// Create a byte array from a decimal.

public static byte[] DecimalToByteArray (decimal src)

{

// Create a MemoryStream as a buffer to hold the binary data. using (MemoryStream stream = new MemoryStream())

C H A P T E R 2 D ATA M A N I P U L AT I O N

37

{

// Create a BinaryWriter to write binary data to the stream. using (BinaryWriter writer = new BinaryWriter(stream))

{

//Write the decimal to the BinaryWriter/MemoryStream. writer.Write(src);

//Return the byte representation of the decimal. return stream.ToArray();

}

}

}

// Create a decimal from a byte array.

public static decimal ByteArrayToDecimal (byte[] src)

{

// Create a MemoryStream containing the byte array. using (MemoryStream stream = new MemoryStream(src))

{

// Create a BinaryReader to read the decimal from the stream. using (BinaryReader reader = new BinaryReader(stream))

{

//Read and return the decimal from the

//BinaryReader/MemoryStream.

return reader.ReadDecimal();

}

}

}

public static void Main()

{

byte[] b = null;

//Convert a bool to a byte array and display. b = BitConverter.GetBytes(true); Console.WriteLine(BitConverter.ToString(b));

//Convert a byte array to a bool and display. Console.WriteLine(BitConverter.ToBoolean(b,0));

//Convert an int to a byte array and display. b = BitConverter.GetBytes(3678); Console.WriteLine(BitConverter.ToString(b));

//Convert a byte array to an int and display. Console.WriteLine(BitConverter.ToInt32(b,0));

//Convert a decimal to a byte array and display. b = DecimalToByteArray(285998345545.563846696m); Console.WriteLine(BitConverter.ToString(b));

//Convert a byte array to a decimal and display. Console.WriteLine(ByteArrayToDecimal(b));

//Wait to continue.

Console.WriteLine("Main method complete. Press Enter");

Console.ReadLine();

Соседние файлы в предмете Программирование на C++