Skip to content

Instantly share code, notes, and snippets.

@ngbrown
Created June 18, 2010 15:56
Show Gist options
  • Save ngbrown/443818 to your computer and use it in GitHub Desktop.
Save ngbrown/443818 to your computer and use it in GitHub Desktop.
C# Extension Methods
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;
}
}
}
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;
}
}
}
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");
}
}
}
}
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