Cover | Table of Contents
System.Messaging.MessageQueue type:
// open an existing message queue
string queuePath = @".\private$\LINQMQ";
MessageQueue messageQueue = new MessageQueue(queuePath);
BinaryMessageFormatter messageFormatter = new BinaryMessageFormatter();
var query = from Message msg in messageQueue
// The first assignment to msg.Formatter is so that we can touch the
// Message object. It assigns the BinaryMessageFormatter to each message
// instance so that it can be read to determine if it matches the criteria.
// Next, a check is performed that the formatter was correctly assigned
// by performing an equality check, which satisfies the Where clause's need
// for a boolean result while still executing the assignment of the formatter.
where ((msg.Formatter = messageFormatter) == messageFormatter) &&
int.Parse(msg.Label) > 5 &&
msg.Body.ToString().Contains('D')
orderby msg.Body.ToString() descending
select msg;
// Check our results for messages with a label > 5 and containing a 'D' in the name
foreach (var msg in query)
{
Console.WriteLine("Label: " + msg.Label + " Body: " + msg.Body);
}
MessageQueue by selecting the messages where the Label is a number greater than 5 and the message body contains a capital letter "D". These messages are then returned sorted by the message body in descending order.varvar keyword and the expression. This allows for implicitly typed local variables.fromfrom keyword sets out the source collection to query against and a range variable to represent a single element from that collection. It is always the first clause in a query operation. This may seem counterintuitive if you are used to SQL and expect select to be first, but if you consider that first we need what to work on before we determine what to return, it makes sense. If we weren't used to how SQL does this already, it would be SQL that seems counterintuitive.
IEnumerable<string> whoLoggedIn =
dailySecurityLog.Where(logEntry => logEntry.Contains("logged in")).Distinct( );
// Union
Console.WriteLine("Employees for all projects");
var allProjectEmployees = project1.Union(project2.Union(project3));
// Intersect
Console.WriteLine("Employees on every project");
var everyProjectEmployees = project1.Intersect(project2.Intersect(project3));
Console.WriteLine("Employees on only one project");
var onlyProjectEmployees = allProjectEmployees.Except(unionIntersect);
// Distinct
string[] dailySecurityLog = {
"Bob logged in",
"Bob logged out",
"Bob logged in",
"Bill logged in",
"Melissa logged in",
"Bob logged out",
"Bill logged out",
"Bill logged in",
"Tim logged in",
"Scott logged in",
"Scott logged out",
"Dave logged in",
"Tim logged out",
"Bob logged in",
"Dave logged out"};
Distinct is an extension method on the System.Linq.EnumerableCompiledQuery.Compile method to build an expression tree that will not have to be parsed each time the query is executed with new parameters:
var GetEmployees =
CompiledQuery.Compile((Northwind db, string ac, string ttl) =>
from employee in db.Employees
where employee.HomePhone.Contains(ac) &&
employee.Title == ttl
select employee);
Northwind dataContext = new Northwind(Settings.Default.NorthwindConnectionString);
GetEmployees is called the first time in the foreach loop). Every other iteration in this loop and in the next loop use the compiled version, avoiding the expression tree parsing:
foreach (var employee in GetEmployees(dataContext, "(206)", "Sales Representative"))
{
Console.WriteLine("{0} {1}",
employee.FirstName, employee.LastName);
}
foreach (var employee in GetEmployees(dataContext, "(71)", "Sales Manager"))
{
Console.WriteLine("{0} {1}",
employee.FirstName, employee.LastName);
}
var for the query declaration, as it was cleaner, but what var actually is in this case is:Func<Northwind, string, string, IQueryable<Employees>>
Func delegate was brought about in the System namespace as part of LINQ, so do not dismay, we are still doing cool newstuff!IEnumerable or IQueryable based result set from Compile, but rather an expression tree. This is the expression tree that represents the potential for a query rather than the query itself. Once we have that tree, LINQ to SQL then has to perform the conversion from the tree to actual SQL that can run against the database. Interestingly enough, if we had put a call to OrderBy query operator, which accepts a custom comparer in order to specify the culture in which to perform comparisons:
// Create CultureInfo for Danish in Denmark.
CultureInfo danish = new CultureInfo("da-DK");
CultureStringComparer comparer = new CultureStringComparer(danish,CompareOptions.
None);
var query = names.OrderBy(n => n, comparer);
System.Globalization namespace. This namespace would be included in order to make the code in the solution run. One example of not using the thread current culture would be in an application that needs to display a sorted list of words in Danish on a version of Windows XP that is set for U.S. English. The current thread in the application may have a CultureInfo for "en-US" and, by default, the sort order for OrderBy will use the current culture sort settings. To specify that this list should sort according to Danish rules, a bit of work is necessary in the form of a custom comparer:CultureStringComparer comparer = new CultureStringComparer(danish,CompareOptions. None);
CultureStringComparer defined as implementing the IComparer<T> interface specialized for strings. This class is used to provide the culture settings for the sort order:
public class CultureStringComparer : IComparer<string>
{
private CultureStringComparer()
{
}
public CultureStringComparer(CultureInfo cultureInfo, CompareOptions options)
{
if (cultureInfo == null)
throw new ArgumentNullException("cultureInfo");
CurrentCultureInfo = cultureInfo;
Options = options;
}
public int Compare(string x, string y)
{
return CurrentCultureInfo.CompareInfo.Compare(x, y, Options);
}
public CultureInfo CurrentCultureInfo { get; set; }
public CompareOptions Options { get; set; }
}
WeightedMovingAverage extension methods in a static class and then call them as part of those collections:
decimal[] prices = new decimal[10] { 13.5M, 17.8M, 92.3M, 0.1M, 15.7M,
19.99M, 9.08M, 6.33M, 2.1M, 14.88M };
Console.WriteLine(prices.WeightedMovingAverage());
double[] dprices = new double[10] { 13.5, 17.8, 92.3, 0.1, 15.7,
19.99, 9.08, 6.33, 2.1, 14.88 };
Console.WriteLine(dprices.WeightedMovingAverage());
float[] fprices = new float[10] { 13.5F, 17.8F, 92.3F, 0.1F, 15.7F,
19.99F, 9.08F, 6.33F, 2.1F, 14.88F };
Console.WriteLine(fprices.WeightedMovingAverage());
int[] iprices = new int[10] { 13, 17, 92, 0, 15,
19, 9, 6, 2, 14 };
Console.WriteLine(iprices.WeightedMovingAverage());
long[] lprices = new long[10] { 13, 17, 92, 0, 15,
19, 9, 6, 2, 14 };
Console.WriteLine(lprices.WeightedMovingAverage());
WeightedMovingAverage for the full range of numeric types, methods for both the nullable and non-nullable numeric types are provided in the LinqExtensions class:
public static class LinqExtensions
{
public static decimal? WeightedMovingAverage(this IEnumerable<decimal?> source)
{
if (source == null)
throw new ArgumentNullException("source");
decimal aggregate = 0.0M;
decimal weight;
int item = 1;
// count how many items are not null and use that
// as the weighting factor
int count = source.Count(val => val.HasValue);
foreach (var nullable in source)
{
if (nullable.HasValue)
{
weight = item / count;
aggregate += nullable.GetValueOrDefault() * weight;
count++;
}
}
if (count > 0)
{
return new decimal?(aggregate / count);
}
return null;
}
// The same method pattern as above is followed for each of the other
// types and their nullable counterparts (double / double?, int / int?, etc.)
#region Extend Average...
}
Northwind dataContext = new Northwind(Settings.Default.NorthwindConnectionString);
ProductsTableAdapter adapter = new ProductsTableAdapter();
Products products = new Products();
adapter.Fill(products._Products);
XElement xmlCategories = XElement.Load("Categories.xml");
var expr = from product in products._Products
where product.Units_In_Stock > 100
join xc in xmlCategories.Elements("Category")
on product.Category_ID equals int.Parse(xc.Attribute("CategoryID").Value)
select new
{
ProductName = product.Product_Name,
Category = xc.Attribute("CategoryName").Value,
CategoryDescription = xc.Attribute("Description").Value
};
foreach (var productInfo in expr)
{
Console.WriteLine("ProductName: " + productInfo.ProductName +
" Category: " + productInfo.Category +
" Category Description: " + productInfo.CategoryDescription);
}
ProductName: Grandma's Boysenberry Spread Category: Condiments Category Description: Sweet and savory sauces, relishes, spreads, and seasonings ProductName: Gustaf's Knäckebröd Category: Grains/Cereals Category Description: Breads, crackers, pasta, and cereal ProductName: Geitost Category: Dairy Products Category Description: Cheeses ProductName: Sasquatch Ale Category: Beverages Category Description: Soft drinks, coffees, teas, beer, and ale ProductName: Inlagd Sill Category: Seafood Category Description: Seaweed and fish ProductName: Boston Crab Meat Category: Seafood Category Description: Seaweed and fish ProductName: Pâté chinois Category: Meat/Poultry Category Description: Prepared meats ProductName: Sirop d'érable Category: Condiments Category Description: Sweet and savory sauces, relishes, spreads, and seasonings ProductName: Röd Kaviar Category: Seafood Category Description: Seaweed and fish ProductName: Rhönbräu Klosterbier Category: Beverages Category Description: Soft drinks, coffees, teas, beer, and ale
CSharpRecipesConfigurationSection recipeConfig =
ConfigurationManager.GetSection("CSharpRecipesConfiguration") as
CSharpRecipesConfigurationSection;
var expr = from ChapterConfigurationElement chapter in
recipeConfig.Chapters.OfType<ChapterConfigurationElement>()
where (chapter.Title.Contains("and")) && ((int.Parse(chapter.Number) % 2)
== 0)
select new
{
ChapterNumber = "Chapter " + chapter.Number,
chapter.Title
};
foreach (var chapterInfo in expr)
{
Console.WriteLine(chapterInfo.ChapterNumber + ": " + chapterInfo.Title);
}
<CSharpRecipesConfiguration CurrentEdition="3"> <Chapters> <add Number="1" Title="Language Integrated Query (LINQ)"/> <add Number="2" Title="Strings and Characters"/> <add Number="3" Title="Classes and Structures"/> <add Number="4" Title="Generics"/> <add Number="5" Title="Collections"/> <add Number="6" Title="Iterators, Partial Types and Partial Methods"/> <add Number="7" Title="Exception Handling"/> <add Number="8" Title="Diagnostics"/> <add Number="9" Title="Delegates, Events, and Functional Programming"/> <add Number="10" Title="Regular Expressions"/> <add Number="11" Title="Data Structures & Algorithms"/> <add Number="12" Title="Filesystem I/O"/> <add Number="13" Title="Reflection"/> <add Number="14" Title="Web"/> <add Number="15" Title="XML"/> <add Number="16" Title="Networking"/> <add Number="17" Title="Security"/> <add Number="18" Title="Threading and Synchronization"/> <add Number="19" Title="Toolbox"/> <add Number="20" Title="Numbers & Enumerations"/> </Chapters> <Editions> <add Number="1" PublicationYear="2004"/> <add Number="2" PublicationYear="2006"/> <add Number="3" PublicationYear="2007"/> </Editions> </CSharpRecipesConfiguration>
Northwind database whose contact is the owner and those owners who placed orders totaling more than $10,000, then create XML containing the company name, contact name, phone number, and total amount of the orders. Finally, the results are written out to the BigSpenders.xml file:
Northwind dataContext = new Northwind(Settings.Default.NorthwindConnectionString);
// Log the generated SQL to the console
dataContext.Log = Console.Out;
var bigSpenders = new XElement("BigSpenders",
from top5 in
(
from customer in
(
from c in dataContext.Customers
// get the customers where the contact is the owner
// and they placed orders
where c.ContactTitle.Contains("Owner")
&& c.Orders.Count > 0
join orderData in
(
from c in dataContext.Customers
// get the customers where the contact is the owner
// and they placed orders
where c.ContactTitle.Contains("Owner")
&& c.Orders.Count > 0
from o in c.Orders
// get the order details
join od in dataContext.OrderDetails
on o.OrderID equals od.OrderID
select new
{
c.CompanyName,
c.CustomerID,
o.OrderID,
// have to calc order value from orderdetails
//(UnitPrice*Quantity as Total)- (Total*Discount)
// as NetOrderTotal
NetOrderTotal = (
(((double)od.UnitPrice) * od.Quantity)
(((double)od.UnitPrice) * od.Quantity) * od.Discount))
}
)
on c.CustomerID equals orderData.CustomerID
into customerOrders
select new
{
c.CompanyName,
c.ContactName,
c.Phone,
// Get the total amount spent by the customer
TotalSpend = customerOrders.Sum(order => order.NetOrderTotal)
}
)
// place focus on the customers that spent > 10000
where customer.TotalSpend > 10000
orderby customer.TotalSpend descending
// only take the top five spenders
select customer).Take(5)
)
// format the data as XML
select new XElement("Customer",
new XAttribute("companyName", top5.CompanyName),
new XAttribute("contactName", top5.ContactName),
new XAttribute("phoneNumber", top5.Phone),
new XAttribute("amountSpent", top5.TotalSpend)));
using (XmlWriter writer = XmlWriter.Create("BigSpenders.xml"))
{
bigSpenders.WriteTo(writer);
}
TakeWhile extension method to retrieve all results until the criteria is matched:
Northwind dataContext = new Northwind(Settings.Default.NorthwindConnectionString);
var query =
dataContext.Suppliers.GroupJoin(dataContext.Products,
s => s.SupplierID, p => p.SupplierID,
(s, products) => new
{
s.CompanyName,
s.ContactName,
s.Phone,
Products = products
}).OrderByDescending(supplierData => supplierData.Products.Count())
.TakeWhile(supplierData => supplierData.Products.Count() > 3);
Console.WriteLine("Suppliers that provide more than three products: {0}", query.
Count());
foreach (var supplierData in query)
{
Console.WriteLine(" Company Name : {0}",supplierData.CompanyName);
Console.WriteLine(" Contact Name : {0}", supplierData.ContactName);
Console.WriteLine(" Contact Phone : {0}", supplierData.Phone);
Console.WriteLine(" Products Supplied : {0}", supplierData.Products.Count());
foreach (var productData in supplierData.Products)
{
Console.WriteLine(" Product: " + productData.ProductName);
}
}
SkipWhile extension method to retrieve all results once the criteria are matched:
Northwind dataContext = new Northwind(Settings.Default.NorthwindConnectionString);
var query =
dataContext.Suppliers.GroupJoin(dataContext.Products,
s => s.SupplierID, p => p.SupplierID,
(s, products) => new
{
s.CompanyName,
s.ContactName,
s.Phone,
Products = products
}).OrderByDescending(supplierData => supplierData.Products.Count())
.SkipWhile(supplierData =>
{
return supplierData.Products.Count() > 3;
});
Console.WriteLine("Suppliers that provide three or less products: {0}",
query.Count());
foreach (var supplierData in query)
{
Console.WriteLine(" Company Name : {0}",supplierData.CompanyName);
Console.WriteLine(" Contact Name : {0}", supplierData.ContactName);
Console.WriteLine(" Contact Phone : {0}", supplierData.Phone);
Console.WriteLine(" Products Supplied : {0}", supplierData.Products.Count());
foreach (var productData in supplierData.Products)
{
Console.WriteLine(" Product: " + productData.ProductName);
}
}
IEnumerable or ICollection but that do support the original nongeneric versions of the IEnumerable or ICollection interfaces, and you would like to be able to query those collections using LINQ.IEnumeration or ICollection interfaces, so it must be provided using either the OfType<T> or Cast<T> extension methods or by specifying the type in the from clause, which inserts a Cast<T> for you. The first example uses Cast<XmlNode> to let LINQ know that the elements in the XmlNodeList returned from XmlDocument.SelectNodes are of type XmlNode. For an example of how to use the OfType<T> extension method, see the Discussion section:
// Make some XML with some types that you can use with LINQ
// that don't support IEnumerable<T> directly
XElement xmlFragment = new XElement("NonGenericLinqableTypes",
new XElement("IEnumerable",
new XElement("System.Collections",
new XElement("ArrayList"),
new XElement("BitArray"),
new XElement("Hashtable"),
new XElement("Queue"),
new XElement("SortedList"),
new XElement("Stack")),*
new XElement("System.Net",
new XElement("CredentialCache")),
new XElement("System.Xml",
new XElement("XmlNodeList")),
new XElement("System.Xml.XPath",
new XElement("XPathNodeIterator"))),
new XElement("ICollection",
new XElement("System.Diagnostics",
new XElement("EventLogEntryCollection")),
new XElement("System.Net",
new XElement("CookieCollection")),
new XElement("System.Security.AccessControl",
new XElement("GenericAcl")),
new XElement("System.Security",
new XElement("PermissionSet"))));
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlFragment.ToString());
// Select the names of the nodes under IEnumerable that have children and are
// named System.Collections and contain a capital S and return that list in
descending order
var query = from node in doc.SelectNodes("/NonGenericLinqableTypes/IEnumerable/*").
Cast<XmlNode>()
where node.HasChildNodes &&
node.Name == "System.Collections"
from XmlNode xmlNode in node.ChildNodes
where xmlNode.Name.Contains('S')
orderby xmlNode.Name descending
select xmlNode.Name;
foreach (string name in query)
{
Console.WriteLine(name);
}
System.String type is a reference type, unlike System.Char, which is a value type and therefore derives from System.ValueType. The string alias is built into C# and can be used instead of the full name.String class; there is also a System.Text.StringBuilder class for performing string manipulations and the System.Text.RegularExpressions namespace for searching strings. This chapter will cover the String class, the System.Text.StringBuilder class, and the Char structure.System.Text.StringBuilder class provides an easy, performance-friendly method of manipulating string objects. Even though this class duplicates much of the functionality of a String class, the StringBuilder class is fundamentally different in that the string contained within the StringBuilder object can actually be modified—you cannot modify a string object. However, this duplicated functionality provides a more efficient manipulation of strings than is obtainable by using the String class.char and wish to determine the kind of character it contains—a letter, digit, number, punctuation character, control character, separator character, symbol, whitespace, or surrogate character (i.e., Unicode characters with a value greater than 64K). Similarly, you have a string variable and want to determine the kind of character in one or more positions within this string.char, use the built-in static methods on the System.Char structure shown here:Char.IsControl Char.IsDigit Char.IsLetter Char.IsNumber Char.IsPunctuation Char.IsSeparator Char.IsSurrogate Char.IsSymbol Char.IsWhitespace
public enum CharKind
{
Digit,
Letter,
Number,
Punctuation,
Unknown
}
System.String type is a reference type, unlike System.Char, which is a value type and therefore derives from System.ValueType. The string alias is built into C# and can be used instead of the full name.String class; there is also a System.Text.StringBuilder class for performing string manipulations and the System.Text.RegularExpressions namespace for searching strings. This chapter will cover the String class, the System.Text.StringBuilder class, and the Char structure.System.Text.StringBuilder class provides an easy, performance-friendly method of manipulating string objects. Even though this class duplicates much of the functionality of a String class, the StringBuilder class is fundamentally different in that the string contained within the StringBuilder object can actually be modified—you cannot modify a string object. However, this duplicated functionality provides a more efficient manipulation of strings than is obtainable by using the String class.char and wish to determine the kind of character it contains—a letter, digit, number, punctuation character, control character, separator character, symbol, whitespace, or surrogate character (i.e., Unicode characters with a value greater than 64K). Similarly, you have a string variable and want to determine the kind of character in one or more positions within this string.char, use the built-in static methods on the System.Char structure shown here:Char.IsControl Char.IsDigit Char.IsLetter Char.IsNumber Char.IsPunctuation Char.IsSeparator Char.IsSurrogate Char.IsSymbol Char.IsWhitespace
public enum CharKind
{
Digit,
Letter,
Number,
Punctuation,
Unknown
}
CharKind enumeration value indicating that type:
static class CharStrExtMethods
{
public static CharKind GetCharKind(this char theChar)
{
if (Char.IsLetter(theChar))
{
return CharKind.Letter;
}
else if (Char.IsNumber(theChar))
{
return CharKind.Number;
}
else if (Char.IsPunctuation(theChar))
{
return CharKind.Punctuation;
}
else
{
return CharKind.Unknown;
}
}
}
GetCharKind extension method performs a series of tests on a character using the Char type's built-in static methods. An enumeration of all the different types of characters is defined and is returned by the GetCharKind method.char structure. The following code modifies the GetCharKind extension method to operate on a string variable while accepting a character position in that string as an argument to the extension method. The character position determines which character in the string is evaluated:char type and use the Equals instance method on the char structure to compare the two characters:
static class CharStrExtMethods
{
public static bool IsCharEqual(this char firstChar, char secondChar)
{
return (IsCharEqual(firstChar, secondChar, false));
}
public static bool IsCharEqual(this char firstChar, char secondChar,
bool caseSensitiveCompare)
{
if (caseSensitiveCompare)
{
return (firstChar.Equals(secondChar));
}
else
{
return (char.ToUpperInvariant(firstChar).Equals(
char.ToUpperInvariant(secondChar)));
}
}
public static bool IsCharEqual(this char firstChar, CultureInfo firstCharCulture,
char secondChar, CultureInfo secondCharCulture)
{
return (IsCharEqual(firstChar, firstCharCulture,
secondChar, secondCharCulture, false));
}
public static bool IsCharEqual(this char firstChar, CultureInfo firstCharCulture,
char secondChar, CultureInfo secondCharCulture,
bool caseSensitiveCompare)
{
if (caseSensitiveCompare)
{
return (firstChar.Equals(secondChar));
}
else
{
return (char.ToUpper(firstChar, firstCharCulture).Equals
(char.ToUpper(secondChar, secondCharCulture)));
}
}
}
IsCharEqual extension method takes only one parameter, which is the character to be compared against the value contained in the current char instance. This extension method then calls the second IsCharEqual method with two parameters. The last parameter on this extension method call defaults to false so that when this method is called, you do not have to pass in a value for the caseSensitiveCompareIndexOf or IndexOfAny in a loop, you can determine how many occurrences of a character or string exist as well as their locations within the string. To find each occurrence of a string in another string using a case-sensitive search, use the following code:
using System;
using System.Collections;
using System.Collections.Generic;
static class CharStrExtMethods
{
public static int[] FindAll(this string matchStr, string searchedStr,
int startPos)
{
int foundPos = -1; // -1 represents not found.
int count = 0;
List<int> foundItems = new List<int>();
do
{
foundPos = searchedStr.IndexOf(matchStr, startPos, StringComparison.
Ordinal);
if (foundPos > -1)
{
startPos = foundPos + 1;
count++;
foundItems.Add(foundPos);
Console.WriteLine("Found item at position: " + foundPos.
ToString());
}
} while (foundPos > -1 && startPos < searchedStr.Length);
return ((int[])foundItems.ToArray());
}
}
FindAll extension method is called with the following parameters:
string data = "Red";
int[] allOccurrences = data.FindAll("BlueTealRedredGreenRedYellow", 0);
"Red" is found at locations 8 and 19 in the string searchedStr. This code uses the IndexOf method inside a loop to iterate through each found matchStr string in the searchStr string.
static class CharStrExtMethods
{
public static int[] FindAll(this char MatchChar, string searchedStr,
int startPos)
{
int foundPos = -1; // -1 represents not found.
int count = 0;
List<int> foundItems = new List<int>();
do
{
foundPos = searchedStr.IndexOf(MatchChar, startPos);
if (foundPos > -1)
{
startPos = foundPos + 1;
count++;
foundItems.Add(foundPos);
Console.WriteLine("Found item at position: " + foundPos.ToString());
}
} while (foundPos > -1 && startPos < searchedStr.Length);
return ((int[])foundItems.ToArray());
}
}
Compare static method on the String class to compare the two strings. Whether the comparison is case-insensitive is determined by the third parameter of one of its overloads. For example:string lowerCase = "abc"; string upperCase = "AbC"; int caseInsensitiveResult = string.Compare(lowerCase, upperCase, StringComparison.CurrentCultureIgnoreCase); int caseSensitiveResult = string.Compare(lowerCase, StringComparison.CurrentCulture);
caseSensitiveResult value is -1 (indicating that lowerCase is "less than" upperCase) and the caseInsensitiveResult is zero (indicating that lowerCase "equals" upperCase).string.Compare method allows you the freedom to choose whether to take into account the case of the strings when comparing them. This method returns an integer indicating the lexical relationship between the two strings. A zero means that the two strings are equal, a negative number means that the first string is less than the second string, and a positive number indicates that the first string is greater than the second string.comparisonType parameter) to either StringComparison.CurrentCultureIgnoreCase or StringComparison.CurrentCulture, you can determine whether the Compare method takes into account the case of both strings when comparing. Setting this parameter to StringComparison.CurrentCulture forces a case-sensitive comparison; setting it to StringComparison.CurrentCulture-IgnoreCase forces a case-insensitive comparison. In the case of the overloaded version of the method with no comparisonType parameter, comparisons are always case-sensitive.EndsWith or StartsWith instance method on a string object. Comparisons with EndsWith and StartsWith are always case-sensitive. The following code compares the value in the string variable head to the beginning of the string Test:string head = "str"; string test = "strVarName"; bool isFound = test.StartsWith(head, StringComparison.Ordinal);
tail to the end of the string test:string tail = "Name"; string test = "strVarName"; bool isFound = test.EndsWith(tail, StringComparison.Ordinal);
isFound Boolean variable is set to true, since each string is found in test.string.Compare method. The following two examples modify the previous two examples by performing a case-insensitive comparison. The first is equivalent to a case-insensitive StartsWith string search:string head = "str"; string test = "strVarName"; int location = string.Compare(head, 0, test, 0, head.Length, true, System.Threading.Thread.CurrentThread.CurrentCulture);
EndsWith string search:
string tail = "Name";
string test = "strVarName";
if (tail.Length <= test.Length)
{
int location = string.Compare(tail, 0, test, (test.Length - tail.Length),
tail.Length, true);
}
else
{
location = -1;
}
Compare method, if a zero is returned, then the search succeeded. If a -1 is returned, the search failed. The following code compares the value in the string variable head to the beginning of the string Test with case-sensitivity turned off:string head = "str"; string test = "strVarName"; bool isFound = test.StartsWith(head, true, System.Threading.Thread.CurrentThread.CurrentCulture);
char or a string value) that needs to be inserted at a specific location inside of a second string.Insert instance method of the String class, a string or char can easily be inserted into a string. For example, in the code fragment:string sourceString = "The Inserted Text is here -><-"; sourceString = sourceString.Insert(28, "Insert-This"); Console.WriteLine(sourceString);
sourceString is inserted between the > and < characters in a second string. The result is:The Inserted Text is here ->Insert-This<-
sourceString between the > and < characters is shown here:string sourceString = "The Inserted Text is here -><-"; char insertChar = '1'; sourceString = sourceString.Insert(28, Convert.ToString(insertChar)); Console.WriteLine(sourceString);
Insert that takes a char value, so converting the char to a string of length one is the next best solution.Insert instance method on the String class. This method is also slower than the others since strings are immutable, and, therefore, a new string object must be created to hold the modified value. In this recipe, the reference to the old string object is then changed to point to the new string object. Note that the Insert method leaves the original string untouched and creates a new string object with the inserted characters.Insert instance method on the StringBuilder class. This method is overloaded to accept all of the built-in types. In addition, the StringBuilder object optimizes string insertion by operating on mutable arrays of characters, lists of immutable strings, and other techniques to defer the creation of new immutable strings until the last possible moment. This insertion will modify the state of the array of characters within the StringBuilder object.StringBuilderRemove instance method on the String class. For example:string name = "Doe, John"; name = name.Remove(3, 1); Console.WriteLine(name);
name variable to refer to it. The string contained in name now looks like this:Doe John
Remove method of the StringBuilder object. The following code modifies the internal state of the StringBuilder object that the str variable references so that its value becomes 12345678:
StringBuilder str = new StringBuilder("1234abc5678", 12);
str.Remove(4, 3);
Console.WriteLine(str);
string commaDelimitedString = "100,200,300,400,500";
commaDelimitedString = commaDelimitedString.Replace(',', ':');
Console.WriteLine(commaDelimitedString);
commaDelimitedString variable refer to it. The string in commaDelimitedString now looks like this:100:200:300:400:500
string theName = "Mary";
string theObject = "car";
string ID = "This <ObjectPlaceholder> is the property of <NamePlaceholder>.";
ID = ID.Replace("<ObjectPlaceholder>", theObject);
ID = ID.Replace("<NamePlaceholder>", theName);
Console.WriteLine(ID);
ID variable refer to it. The string in ID now looks like this:This car is the property of Mary.
Replace method of the StringBuilder class instead. For example:
string newName = "John Doe";
StringBuilder str = new StringBuilder("name = <NAME>");
str.Replace("<NAME>", newName);
Console.WriteLine(str.ToString());
str.Replace('=', ':');
Console.WriteLine(str.ToString());
str = new StringBuilder("name1 = <FIRSTNAME>, name2 = <FIRSTNAME>");
str.Replace("<FIRSTNAME>", newName, 7, 12);
Console.WriteLine(str.ToString());
str.Replace('=', ':', 0, 7);
Console.WriteLine(str.ToString());
byte[] representing some binary information, such as a bitmap. You need to encode this data into a string so that it can be sent over a binary-unfriendly transport, such as email.Convert.ToBase64String on the Convert class, a byte[] may be encoded to its String equivalent:
using System;
using System.IO;
static class CharStrExtMethods
{
public static string Base64EncodeBytes(this byte[] inputBytes)
{
return (Convert.ToBase64String(inputBytes));
}
}
Convert class makes encoding between a byte[] and a String a simple matter. The parameters for this method are quite flexible. It provides the ability to start and stop the conversion at any point in the input byte array.
byte[] image = null;
using (FileStream fstrm = new FileStream(@"C:\WINNT\winnt.bmp",
FileMode.Open, FileAccess.Read))
{
using (BinaryReader reader = new BinaryReader(fstrm))
{
image = new byte[reader.BaseStream.Length];
for (int i = 0; i < reader.BaseStream.Length; i++)
{
image[i] = reader.ReadByte();
}
}
}
string bmpAsString = image.Base64EncodeBytes();
bmpAsString string as an embedded MIME attachment in an email message, you must insert a CRLF on each 76-character boundary.