Introduction
Extension methods (a new feature of C# 3.0) are useful as they enable to "add" methods to a class without modifying its source code. Such methods behave (from a point of writing code and intellisense) like member methods. This is very useful for built-in .NET classes or third-party libraries. Hundreds of articles have been written about this; the aim of this article is not to introduce extension methods, but to show a collection of several most useful extension methods for the System.String
class.
This article brings a small library (a code file and unit tests for this code). Some of the extension methods have been collected from various websites, and some were written by me. Unit tests are presented for demonstration purposes.
Background
For those who don't know about extension methods, I suggest reading this nice article on Wikipedia.
Using the Code
Let me introduce the source code without much delay. The first method was written by David Hayden and checks if an email ID is in valid format.
public static bool IsValidEmailAddress(this string s)
{
return new Regex(@"^[\w-\.]+@([\w-]+\.)+[\w-]{2,6}$").IsMatch(s);
}
The counterpart test method is the following:
[TestMethod()]
public void IsValidEmailAddressTest()
{
Assert.IsTrue("yellowdog@someemail.uk".IsValidEmailAddress());
Assert.IsTrue("yellow.444@email4u.co.uk".IsValidEmailAddress());
Assert.IsFalse("adfasdf".IsValidEmailAddress());
Assert.IsFalse("asd@asdf".IsValidEmailAddress());
}
I have found a lot of UR validation functions, but not all of them seemed to be OK. This method is inspired by a Regular Expression written by bb, which seems to work fine.
public static bool IsValidUrl(this string url)
{
string strRegex = "^(https?://)"
+ "?(([0-9a-z_!~*'().&=+$%-]+: )?[0-9a-z_!~*'().&=+$%-]+@)?"
+ @"(([0-9]{1,3}\.){3}[0-9]{1,3}"
+ "|"
+ @"([0-9a-z_!~*'()-]+\.)*"
+ @"([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]"
+ @"(\.[a-z]{2,6})?)"
+ "(:[0-9]{1,5})?"
+ "((/?)|"
+ "(/[0-9a-z_!~*'().;?:@&=+$,%#-]+)+/?)$";
return new Regex(strRegex).IsMatch(url);
}
The counterpart test method is the following:
[TestMethod()]
public void IsValidUrlTest()
{
Assert.IsTrue("http://www.codeproject.com".IsValidUrl());
Assert.IsTrue("https://www.codeproject.com/#some_anchor".IsValidUrl());
Assert.IsTrue("https://localhost".IsValidUrl());
Assert.IsTrue("http://www.abcde.nf.net/signs-banners.jpg".IsValidUrl());
Assert.IsTrue("http://aa-bbbb.cc.bla.com:80800/test/" +
"test/test.aspx?dd=dd&id=dki".IsValidUrl());
Assert.IsFalse("http:wwwcodeprojectcom".IsValidUrl());
Assert.IsFalse("http://www.code project.com".IsValidUrl());
}
I have written a third method to test if the user provides the existing homepage:
public static bool UrlAvailable(this string httpUrl)
{
if (!httpUrl.StartsWith("http://") || !httpUrl.StartsWith("https://"))
httpUrl = "http://" + httpUrl;
try
{
HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(httpUrl);
myRequest.Method = "GET";
myRequest.ContentType = "application/x-www-form-urlencoded";
HttpWebResponse myHttpWebResponse =
(HttpWebResponse)myRequest.GetResponse();
return true;
}
catch
{
return false;
}
}
The counterpart test method is the following:
public void UrlAvailableTest()
{
Assert.IsTrue("www.codeproject.com".UrlAvailable());
Assert.IsFalse("www.asjdfalskdfjalskdf.com".UrlAvailable());
}
The reversing string example can be found on Wikipedia. This version without the cycle looks better.
public static string Reverse(this string input)
{
char[] chars = input.ToCharArray();
Array.Reverse(chars);
return new String(chars);
}
The counterpart test method is as follows:
public void ReverseTest()
{
string input = "yellow dog";
string expected = "god wolley";
string actual = input.Reverse();
Assert.AreEqual(expected, actual);
}
Sometimes, you need to provide a preview of a long text. This can be done using this Reduce
extension method:
public static string Reduce(this string s, int count, string endings)
{
if (count < endings.Length)
throw new Exception("Failed to reduce to less then endings length.");
int sLength = s.Length;
int len = sLength;
if (endings != null)
len += endings.Length;
if (count > sLength)
return s;
s = s.Substring(0, sLength - len + count);
if (endings != null)
s += endings;
return s;
}
The counterpart test method is the following:
[TestMethod()]
public void ReduceTest()
{
string input = "The quick brown fox jumps over the lazy dog";
int count = 10;
string endings = "...";
string expected = "The qui...";
string actual = input.Reduce(count, endings);
Assert.AreEqual(expected, actual);
}
Sometimes you need to parse a phone number or a price, and the user might have interposed the string with spaces. To not boss the user about, and to avoid duplicating test conditions, you can use the RemoveSpaces
extension method when parsing numbers.
public static string RemoveSpaces(this string s)
{
return s.Replace(" ", "");
}
The counterpart test method is the following:
[TestMethod()]
public void RemoveSpacesTest()
{
string input = "yellow dog" + Environment.NewLine + "black cat";
string expected = "yellowdog" + Environment.NewLine + "blackcat";
string actual = input.RemoveSpaces();
Assert.AreEqual(expected, actual);
}
If you need to ensure the user input to be a number and you want to be tolerant of the number format, use the IsNumber
extension.
public static bool IsNumber(this string s, bool floatpoint)
{
int i;
double d;
string withoutWhiteSpace = s.RemoveSpaces();
if (floatpoint)
return double.TryParse(withoutWhiteSpace, NumberStyles.Any,
Thread.CurrentThread.CurrentUICulture , out d);
else
return int.TryParse(withoutWhiteSpace, out i);
}
The counterpart test method is the following:
[TestMethod()]
public void IsNumberTest()
{
Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
Assert.IsTrue("12345".IsNumber(false));
Assert.IsTrue(" 12345".IsNumber(false));
Assert.IsTrue("12.345".IsNumber(true));
Assert.IsTrue(" 12,345 ".IsNumber(true));
Assert.IsTrue("12 345".IsNumber(false));
Assert.IsFalse("tractor".IsNumber(true));
}
The more restrictive version of the IsNumber
method is IsNumberOnly
, which ensures that all characters are digits, possibly float point. This could also be done using LINQ via s.ToCharArray().Where(...).Count() == 0
.
public static bool IsNumberOnly(this string s, bool floatpoint)
{
s = s.Trim();
if (s.Length == 0)
return false;
foreach (char c in s)
{
if (!char.IsDigit(c))
{
if (floatpoint && (c == '.' || c == ','))
continue;
return false;
}
}
return true;
}
The counterpart test method is the following:
[TestMethod()]
public void IsNumberOnlyTest()
{
Assert.IsTrue("12345".IsNumberOnly(false));
Assert.IsTrue(" 12345".IsNumberOnly(false));
Assert.IsTrue("12.345".IsNumberOnly(true));
Assert.IsTrue(" 12,345 ".IsNumberOnly(true));
Assert.IsFalse("12 345".IsNumberOnly(false));
Assert.IsFalse("tractor".IsNumberOnly(true));
}
Michael Kaplan describes a very useful method for removing diacritics (accents) from strings. It is useful when implementing URL rewriting, and you need to generate valid and readable URLs.
public static string RemoveDiacritics(this string s)
{
string stFormD = s.Normalize(NormalizationForm.FormD);
StringBuilder sb = new StringBuilder();
for (int ich = 0; ich < stFormD.Length; ich++)
{
UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(stFormD[ich]);
if (uc != UnicodeCategory.NonSpacingMark)
{
sb.Append(stFormD[ich]);
}
}
return (sb.ToString().Normalize(NormalizationForm.FormC));
}
The counterpart test method is the following:
[TestMethod()]
public void RemoveDiacriticsTest()
{
string actual = input.RemoveDiacritics();
Assert.AreEqual(expected, actual);
}
When I was programming in PHP, Nl2Br
was a very useful PHP function. This one was posted by DigiMortal.
public static string Nl2Br(this string s)
{
return s.Replace("\r\n", "<br />").Replace("\n", "<br />");
}
The counterpart test method is the following:
[TestMethod()]
public void Nl2BrTest()
{
string input = "yellow dog" + Environment.NewLine + "black cat";
string expected = "yellow dog<br />black cat";
string actual = input.Nl2Br();
Assert.AreEqual(expected, actual);
}
The MD5
function can be used in almost every application.
static MD5CryptoServiceProvider s_md5 = null;
public static string MD5(this string s)
{
if( s_md5 == null)
s_md5 = new MD5CryptoServiceProvider();
Byte[] newdata = Encoding.Default.GetBytes(s);
Byte[] encrypted = s_md5.ComputeHash(newdata);
return BitConverter.ToString(encrypted).Replace("-", "").ToLower();
}
The counterpart test method is the following:
[TestMethod()]
public void MD5Test()
{
string input = "The quick brown fox jumps over the lazy dog";
string expected = "9e107d9d372bb6826bd81d3542a419d6";
string actual = input.MD5();
Assert.AreEqual(expected, actual);
}
Points of Interest
While writing this article, I have found an extensive library here. Unfortunately, some links don't work.
History
- 18 Nov 2008
- Definition of extension methods was changed not to propagate a misstatement about them being members.
- Removed
CreateDirIfNotExistsTest
, it was really useless. - URL Regex changed to match http://localhost
- 25 Nov 2008
- Changed Invariant Culture to CurrentUI Culture (thanks to x2develop.com).
- Changed email regex to match .museum domain.
- Changed MD5 method to be more effective (thanks to Juan).