Created
June 18, 2010 15:56
-
-
Save ngbrown/443818 to your computer and use it in GitHub Desktop.
C# Extension Methods
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Helpers | |
{ | |
using System; | |
/// <summary> | |
/// A static class of extension methods for <see cref="Array"/>. | |
/// </summary> | |
public static class ArrayHelpers | |
{ | |
/// <summary> | |
/// Sets all values. | |
/// </summary> | |
/// <typeparam name="T">The type of the elements of the array that will be modified.</typeparam> | |
/// <param name="array">The one-dimensional, zero-based array</param> | |
/// <param name="value">The value.</param> | |
/// <returns>A reference to the changed array.</returns> | |
public static T[] SetAllValues<T>(this T[] array, T value) | |
{ | |
for (int i = 0; i < array.Length; i++) | |
{ | |
array[i] = value; | |
} | |
return array; | |
} | |
/// <summary> | |
/// Get the array slice between the two indexes. | |
/// Inclusive for start index, exclusive for end index. | |
/// </summary> | |
/// <typeparam name="T">The type of the elements of the array.</typeparam> | |
/// <param name="array">The one-dimensional, zero-based array that will be sliced from.</param> | |
/// <param name="index">The start index.</param> | |
/// <param name="end">The end index. If end is negative, it is treated like length.</param> | |
/// <returns>The resulting array.</returns> | |
public static T[] Slice<T>(this T[] array, int index, int end) | |
{ | |
// Handles negative ends | |
int len; | |
if (end == 0) | |
{ | |
throw new ArgumentOutOfRangeException("end", end, "must be a positive index or a length (indicated by negative), not 0"); | |
} | |
else if (end > 0) | |
{ | |
len = end - index; | |
} | |
else | |
{ | |
len = -end; | |
end = index + len; | |
} | |
// Return new array | |
T[] res = new T[len]; | |
for (int i = 0; i < len; i++) | |
{ | |
res[i] = array[i + index]; | |
} | |
return res; | |
} | |
/// <summary> | |
/// Checks if the Arrays are equal. | |
/// </summary> | |
/// <typeparam name="T">Array type.</typeparam> | |
/// <param name="a">The <see cref="Array"/> that contains data to compare with.</param> | |
/// <param name="b">The <see cref="Array"/> that contains data to compare to.</param> | |
/// <param name="index">A 32-bit integer that represents the index in the arrays at which comparing begins.</param> | |
/// <param name="length">A 32-bit integer that represents the number of elements to compare.</param> | |
/// <returns> | |
/// Returns <c>true</c> if all element match and <c>false</c> otherwise. | |
/// </returns> | |
public static bool ArrayEqual<T>(this T[] a, T[] b, int index, int length) | |
where T : IEquatable<T> | |
{ | |
// If both are null, or both are same instance, return true. | |
if (System.Object.ReferenceEquals(a, b)) | |
{ | |
return true; | |
} | |
// If one is null, but not both, return false. | |
if (((object)a == null) || ((object)b == null)) | |
{ | |
return false; | |
} | |
if (a.Length < index + length || b.Length < index + length) | |
{ | |
return false; | |
} | |
for (int i = index; i < length; i++) | |
{ | |
if (!a[i].Equals(b[i])) | |
{ | |
return false; | |
} | |
} | |
return true; | |
} | |
/// <summary> | |
/// Check that the arrays are equal. | |
/// </summary> | |
/// <typeparam name="T">Type implementing IEquatable. Value types work.</typeparam> | |
/// <param name="a">First array</param> | |
/// <param name="b">Second array</param> | |
/// <returns> | |
/// <c>true</c> if contents are equal, otherwise <c>false</c>. | |
/// </returns> | |
public static bool ArrayEqual<T>(this T[] a, T[] b) | |
where T : IEquatable<T> | |
{ | |
// If both are null, or both are same instance, return true. | |
if (System.Object.ReferenceEquals(a, b)) | |
{ | |
return true; | |
} | |
// If one is null, but not both, return false. | |
if (((object)a == null) || ((object)b == null)) | |
{ | |
return false; | |
} | |
if (a.LongLength != b.LongLength) | |
{ | |
return false; | |
} | |
for (long i = 0; i < a.LongLength; i++) | |
{ | |
if (!a[i].Equals(b[i])) | |
{ | |
return false; | |
} | |
} | |
return true; | |
} | |
/// <summary> | |
/// Extracts specified number of the right-most elements from a source array. | |
/// </summary> | |
/// <typeparam name="T">Array type.</typeparam> | |
/// <param name="value">The source buffer.</param> | |
/// <param name="countFromRight">The number of bytes to copy.</param> | |
/// <returns>The resulting buffer.</returns> | |
/// <exception cref="ArgumentNullException">value is a null reference.</exception> | |
/// <exception cref="ArgumentOutOfRangeException">countFromRight is greater than the length of the value array.</exception> | |
/// <example> | |
/// <code lang="CS"> | |
/// <![CDATA[ | |
/// byte[] flashAddressByte = BitHelper.Right(BitHelper.BytesToLittleEndian(BitConverter.GetBytes(BitHelper.Shift(flashAddress >> 9, 15, 0))), 2);]]> | |
/// </code> | |
/// </example> | |
public static T[] Right<T>(this T[] value, int countFromRight) | |
where T : struct | |
{ | |
T[] retVal = new T[countFromRight]; | |
Buffer.BlockCopy(value, value.Length - countFromRight, retVal, 0, countFromRight); | |
return retVal; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Helpers | |
{ | |
using System; | |
using System.Collections; | |
/// <summary> | |
/// Extension methods for <see cref="BitArray"/> objects. | |
/// </summary> | |
public static class BitArrayHelpers | |
{ | |
/// <summary> | |
/// Finds the index for a given match function. | |
/// </summary> | |
/// <param name="gateArray">The gate array.</param> | |
/// <param name="match">The match.</param> | |
/// <returns>The index of the first found value; else -1.</returns> | |
public static int FindIndex(this BitArray gateArray, Predicate<bool> match) | |
{ | |
var foundIndex = -1; | |
for (int i = 0; i < gateArray.Count; i++) | |
{ | |
if (match(gateArray[i])) | |
{ | |
foundIndex = i; | |
break; | |
} | |
} | |
return foundIndex; | |
} | |
/// <summary> | |
/// Finds the index for a given match function. | |
/// </summary> | |
/// <param name="gateArray">The gate array.</param> | |
/// <param name="startIndex">The start index.</param> | |
/// <param name="match">The match.</param> | |
/// <returns>The index of the first found value; else -1.</returns> | |
public static int FindIndex(this BitArray gateArray, int startIndex, Predicate<bool> match) | |
{ | |
var foundIndex = -1; | |
for (int i = startIndex; i < gateArray.Count; i++) | |
{ | |
if (match(gateArray[i])) | |
{ | |
foundIndex = i; | |
break; | |
} | |
} | |
return foundIndex; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Helpers | |
{ | |
using System; | |
using System.Collections; | |
using System.Text; | |
/// <summary> | |
/// A static class of extension methods for handling individual bits. | |
/// </summary> | |
public static class BitHelpers | |
{ | |
/// <summary> | |
/// Reverses the specified value up to the number of bits. | |
/// </summary> | |
/// <param name="i">The bits to reverse.</param> | |
/// <param name="bits">The number of bits to reverse from the left (lsb). Other bits are discarded.</param> | |
/// <returns>The reversed bits value.</returns> | |
public static ulong Reverse(this ulong i, int bits) | |
{ | |
i = ((i >> 1) & 0x5555555555555555) | ((i & 0x5555555555555555) << 1); | |
i = ((i >> 2) & 0x3333333333333333) | ((i & 0x3333333333333333) << 2); | |
i = ((i >> 4) & 0x0F0F0F0F0F0F0F0F) | ((i & 0x0F0F0F0F0F0F0F0F) << 4); | |
i = ((i >> 8) & 0x00FF00FF00FF00FF) | ((i & 0x00FF00FF00FF00FF) << 8); | |
i = ((i >> 16) & 0x0000FFFF0000FFFF) | ((i & 0x0000FFFF0000FFFF) << 16); | |
i = (i >> 32) | (i << 32); | |
return i >> (64 - bits); | |
} | |
/// <summary> | |
/// Returns a <see cref="String"/> formatted as hex with spaces that represents the current ushort[] array. | |
/// </summary> | |
/// <param name="data">The byte[] array.</param> | |
/// <returns>A <see cref="String"/> formatted as hex with spaces that represents the current ushort[] array.</returns> | |
public static string ToStringHex(this ushort[] data) | |
{ | |
StringBuilder datastring = new StringBuilder(data.Length * 5); | |
for (var i = 0; i < data.Length; i++) | |
{ | |
datastring.AppendFormat("{0,4:x4} ", data[i]); | |
} | |
// remove the last space | |
datastring.Remove(datastring.Length - 1, 1); | |
return datastring.ToString(); | |
} | |
public static byte[] ToBytes(this ushort[] data) | |
{ | |
var bytes = new byte[data.Length * 2]; | |
for (int i = 0; i < data.Length; i++) | |
{ | |
var shortWord = data[i]; | |
bytes[i * 2] = (byte)((shortWord >> 8) & 0xFF); | |
bytes[i * 2 + 1] = (byte)(shortWord & 0xFF); | |
} | |
return bytes; | |
} | |
/// <summary> | |
/// Returns a <see cref="String"/> formatted as hex with spaces that represents the current byte[] array. | |
/// </summary> | |
/// <param name="data">The byte[] array.</param> | |
/// <returns>A <see cref="String"/> formatted as hex with spaces that represents the current byte[] array.</returns> | |
public static string ToStringHex(this byte[] data) | |
{ | |
StringBuilder datastring = new StringBuilder(data.Length * 3); | |
for (var i = 0; i < data.Length; i++) | |
{ | |
datastring.AppendFormat("{0,2:x2} ", data[i]); | |
} | |
// remove the last space | |
datastring.Remove(datastring.Length - 1, 1); | |
return datastring.ToString(); | |
} | |
/// <summary> | |
/// Returns a <see cref="String"/> formatted in ASCII characters that represents the current byte[] array. | |
/// </summary> | |
/// <param name="data">The byte[] array.</param> | |
/// <returns>A <see cref="String"/> formatted in ASCII characters that represents the current byte[] array.</returns> | |
public static string ToStringAscii(this byte[] data) | |
{ | |
StringBuilder datastring = new StringBuilder(data.Length); | |
for (var i = 0; i < data.Length; i++) | |
{ | |
if (20 > data[i]) | |
{ | |
datastring.Append((char)0xB7); | |
} | |
else | |
{ | |
datastring.Append((char)data[i]); | |
} | |
} | |
return datastring.ToString(); | |
} | |
/// <summary> | |
/// Converts the numeric value of input value to its equivalent binary string | |
/// representation. Prepends '0b' to the string. | |
/// </summary> | |
/// <returns> | |
/// A String of binary sets separated by spaces, where each pair represents the | |
/// corresponding element in value; for example, "0b1101 1100". | |
/// </returns> | |
/// <exception cref="ArgumentOutOfRangeException">Length not divisible by 8.</exception> | |
/// <exception cref="ArgumentNullException">value is a null reference</exception> | |
/// <example> | |
/// <code lang="CS"> | |
/// string dataBinary = BitHelper.FormatBinary(data, 16); | |
/// </code> | |
/// </example> | |
/// <param name="value">Contains the bits to output right aligned in the value.</param> | |
/// <param name="length">The length of the bits in the value. Must be divisible by 8.</param> | |
public static string ToStringBinary(this int value, int length) | |
{ | |
if ((length % 8) != 0) | |
{ | |
throw new ArgumentOutOfRangeException("length", length, "length not divisable by 8"); | |
} | |
byte[] valueByteLength = ArrayHelpers.Right(BytesToLittleEndian(BitConverter.GetBytes(value)), length / 8); | |
BitArray valueBits = new BitArray(length); | |
// Build the BitArray the way we want it. | |
for (int i = 0; i < length; i++) | |
{ | |
valueBits.Set(i, ((valueByteLength[i / 8] >> (7 - (i % 8))) & 0x01) != 0); | |
} | |
// Format BitArray to string with space every 4 bits (starting from right). | |
string valueString = string.Empty; | |
{ | |
int posNeg = valueBits.Length; | |
foreach (bool bit in valueBits) | |
{ | |
if (posNeg % 4 == 0 && posNeg > 0 && posNeg != valueBits.Length) | |
{ | |
valueString += " "; | |
} | |
posNeg--; | |
valueString += bit ? "1" : "0"; | |
} | |
} | |
return "0b" + valueString; | |
} | |
/// <summary> | |
/// Shift a single bit to the left within a byte | |
/// </summary> | |
/// <returns>byte with value shifted</returns> | |
/// <exception cref="ArgumentOutOfRangeException">'shift' needs to be at least 0"</exception> | |
/// <example> | |
/// <code lang="CS"> | |
/// <![CDATA[ | |
/// byte writeBuffer = (byte)((BitHelper.Shift((int)txRxMode, 1, 0) | |
/// | BitHelper.Shift((int)powerMode, 1, 2) | |
/// | BitHelper.Shift(on12SecondFrameBoundary, 3) | |
/// | BitHelper.Shift(on24HourFrameBoundary, 4)) & 0x00FF);]]> | |
/// </code> | |
/// </example> | |
/// <param name="value">Bit to shift into output word.</param> | |
/// <param name="shift">Amount to shift to the left</param> | |
public static byte ShiftToByte(this bool value, int shift) | |
{ | |
if (shift < 0) | |
{ | |
throw new ArgumentOutOfRangeException("shift", shift, "'shift' needs to be at least 0"); | |
} | |
return (byte)(((value ? 1 : 0) << shift) & (0x1 << shift)); | |
} | |
/// <summary> | |
/// Shift a value to the left within a byte | |
/// </summary> | |
/// <returns>byte with value shifted</returns> | |
/// <exception cref="ArgumentOutOfRangeException"> | |
/// 'shift' needs to be at least 0 and 'width' needs to be at least 1 | |
/// </exception> | |
/// <example> | |
/// <code lang="CS"> | |
/// <![CDATA[ | |
/// byte writeBuffer = (byte)((BitHelper.Shift((byte)txRxMode, 1, 0) | |
/// | BitHelper.Shift((byte)powerMode, 1, 2) | |
/// | BitHelper.Shift(on12SecondFrameBoundary, 3) | |
/// | BitHelper.Shift(on24HourFrameBoundary, 4)) & 0x00FF);]]> | |
/// </code> | |
/// </example> | |
/// <param name="value">Contains the bits to output right aligned in the value.</param> | |
/// <param name="width">The length of the bits in the value.</param> | |
/// <param name="shift">Amount to shift to the left</param> | |
public static byte Shift(this byte value, int width, int shift) | |
{ | |
if (shift < 0) | |
{ | |
throw new ArgumentOutOfRangeException("shift", shift, "'shift' needs to be at least 0"); | |
} | |
if (width < 1) | |
{ | |
throw new ArgumentOutOfRangeException("width", width, "'width' needs to be at least 1"); | |
} | |
return (byte)((value << shift) & (((0x1 << width) - 1) << shift)); | |
} | |
/// <summary> | |
/// Converts a byte array between native ordered and big endian. | |
/// </summary> | |
/// <remarks> | |
/// On a little endian machine (Windows), the bytes are flipped, otherwise | |
/// they remain the same. | |
/// </remarks> | |
/// <returns>An array of bytes.</returns> | |
/// <exception cref="ArgumentOutOfRangeException"> | |
/// Too many bytes passed in. Limit to 4. | |
/// </exception> | |
/// <example> | |
/// <code lang="CS"> | |
/// <![CDATA[ | |
/// byte[] flashAddressByte = BitHelper.Right(BitHelper.BytesToLittleEndian(BitConverter.GetBytes(BitHelper.Shift(flashAddress >> 9, 15, 0))), 2);]]> | |
/// </code> | |
/// </example> | |
/// <param name="value">An array of bytes.</param> | |
public static byte[] BytesToLittleEndian(this byte[] value) | |
{ | |
byte[] newValue = (byte[])value.Clone(); | |
// If the system architecture is little-endian (that is, little end first), | |
// reverse the byte array. | |
if (BitConverter.IsLittleEndian) | |
{ | |
Array.Reverse(newValue); | |
} | |
return newValue; | |
} | |
/// <summary> | |
/// Shifts the bits within the byte array to the left. | |
/// </summary> | |
/// <returns>An array of bytes.</returns> | |
/// <example> | |
/// <code lang="CS"> | |
/// byte[] result = BitHelper.ShiftBytesLeft(requestWord, 0); | |
/// </code> | |
/// </example> | |
/// <param name="value">An array of bytes.</param> | |
/// <param name="shift">Number of bits to shift to the left.</param> | |
public static byte[] ShiftBytesLeft(this byte[] value, int shift) | |
{ | |
byte[] newValue = new byte[value.Length]; | |
byte overflow = 0x00; | |
// Count down to 0 | |
for (int i = value.Length - 1; i >= 0; i--) | |
{ | |
int byteEndPosition = (i * 8) - shift + 7; | |
int resultBytePosition = byteEndPosition / 8; | |
if (byteEndPosition >= 0) | |
{ | |
newValue[resultBytePosition] = (byte)(value[i] << (shift % 8)); | |
newValue[resultBytePosition] |= overflow; | |
overflow = (byte)(((value[i] << (shift % 8)) & 0xFF00) >> 8); | |
} | |
} | |
return newValue; | |
} | |
/// <summary> | |
/// Shifts the bits within the byte array to the right. | |
/// </summary> | |
/// <returns>An array of bytes.</returns> | |
/// <example> | |
/// <code lang="CS"> | |
/// byte[] result = BitHelper.ShiftBytesRight(requestWord, 0); | |
/// </code> | |
/// </example> | |
/// <param name="value">An array of bytes.</param> | |
/// <param name="shift">Number of bits to shift to the right.</param> | |
public static byte[] ShiftBytesRight(this byte[] value, int shift) | |
{ | |
byte[] newValue = new byte[value.Length]; | |
byte overflow = 0x00; | |
// Count up to length | |
for (int i = 0; i < value.Length; i++) | |
{ | |
int byteStartPosition = (i * 8) + shift; | |
int resultBytePosition = byteStartPosition / 8; | |
if (resultBytePosition < value.Length) | |
{ | |
newValue[resultBytePosition] = (byte)(value[i] >> (shift % 8)); | |
newValue[resultBytePosition] |= overflow; | |
overflow = (byte)((value[i] << (8 - (shift % 8))) & 0xFF); | |
} | |
} | |
return newValue; | |
} | |
/// <summary> | |
/// Extracts a value from the array of bytes. | |
/// </summary> | |
/// <returns>Value extracted, up to 32 bits.</returns> | |
/// <example> | |
/// <code lang="CS"> | |
/// int firmwareDateDay = BitHelper.GetValueInBytes(thisReport, 23 - 12, 5); | |
/// </code> | |
/// </example> | |
/// <param name="value">An array of bytes in big endian order.</param> | |
/// <param name="startBit">0 index of bits starting from the left.</param> | |
/// <param name="length">Length of bits to extract.</param> | |
public static int GetValueInBytes(this byte[] value, int startBit, int length) | |
{ | |
int byteSize = (length / 8) + (length % 8 > 0 ? 1 : 0); | |
if (byteSize > 4) | |
{ | |
throw new ArgumentOutOfRangeException("length", length, "length must be 32 bits or less"); | |
} | |
// what size value are we putting this into? Up to 8 bytes. | |
int byteSizeUp = (byteSize <= 1) ? 1 : ((byteSize <= 2) ? 2 : ((byteSize <= 4) ? 4 : 8)); | |
// holds the value we want, right aligned, with garbage to the left | |
byte[] shiftedValue = new byte[byteSizeUp]; | |
Array.Copy(ShiftBytesRight(value, (value.Length * 8) - startBit - length), value.Length - byteSizeUp, shiftedValue, 0, byteSizeUp); | |
// Convert to little endian for the BitConverter | |
shiftedValue = BytesToLittleEndian(shiftedValue); | |
// TODO: Extend for signed numbers? | |
switch (byteSizeUp) | |
{ | |
case 1: | |
byte mask8 = (byte)((1 << length) - 1); | |
return shiftedValue[0] & mask8; | |
case 2: | |
short mask16 = (short)((1 << length) - 1); | |
return BitConverter.ToInt16(shiftedValue, 0) & mask16; | |
case 4: | |
int mask32 = (1 << length) - 1; | |
return BitConverter.ToInt32(shiftedValue, 0) & mask32; | |
case 8: | |
long mask64 = ((long)1 << length) - 1; | |
return (int)(BitConverter.ToInt64(shiftedValue, 0) & mask64); | |
default: | |
throw new InvalidOperationException("Something is wrong with the conversion"); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace Helpers | |
{ | |
using System; | |
using System.ComponentModel; | |
/// <summary> | |
/// A static class of extension methods for handling <see cref="Enum"/> values. | |
/// </summary> | |
public static class EnumHelpers | |
{ | |
/// <summary> | |
/// Checks if the target enumeration includes the specified flag. | |
/// </summary> | |
/// <typeparam name="TEnum">The type of the enum.</typeparam> | |
/// <param name="target">The target.</param> | |
/// <param name="flags">The flags.</param> | |
/// <returns> | |
/// <b>true</b> if match, otherwise <b>false</b>. | |
/// </returns> | |
/// <remarks> | |
/// From http://www.codeproject.com/KB/cs/fun-with-cs-extensions.aspx?msg=2838918#xx2838918xx | |
/// </remarks> | |
public static bool Includes<TEnum>(this TEnum target, TEnum flags) | |
where TEnum : IComparable, IConvertible, IFormattable | |
{ | |
if (target.GetType() != flags.GetType()) | |
{ | |
throw new ArgumentException("Enum type mismatch", "flags"); | |
} | |
long a = Convert.ToInt64(target); | |
long b = Convert.ToInt64(flags); | |
return (a & b) == b; | |
} | |
/// <summary> | |
/// Checks if the target enumeration includes any of the specified flags | |
/// </summary> | |
/// <typeparam name="TEnum">The type of the enum.</typeparam> | |
/// <param name="target">The target.</param> | |
/// <param name="flags">The flags.</param> | |
/// <returns> | |
/// <b>true</b> if match, otherwise <b>false</b>. | |
/// </returns> | |
/// <remarks> | |
/// From http://www.codeproject.com/KB/cs/fun-with-cs-extensions.aspx?msg=2838918#xx2838918xx | |
/// </remarks> | |
public static bool IncludesAny<TEnum>(this TEnum target, TEnum flags) | |
where TEnum : IComparable, IConvertible, IFormattable | |
{ | |
if (target.GetType() != flags.GetType()) | |
{ | |
throw new ArgumentException("Enum type mismatch", "flags"); | |
} | |
long a = Convert.ToInt64(target); | |
long b = Convert.ToInt64(flags); | |
return (a & b) != 0L; | |
} | |
/// <summary> | |
/// Returns the description string. | |
/// </summary> | |
/// <param name="val">The enumeration.</param> | |
/// <returns>The value of the <see cref="DescriptionAttribute"/> associated with the <c>enum</c> or the ToString of the <c>enum</c>.</returns> | |
public static string ToDescriptionString(this Enum val) | |
{ | |
var attributes = (DescriptionAttribute[])val.GetType().GetField(val.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false); | |
return attributes.Length > 0 ? attributes[0].Description : val.ToString(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment