using System;
using System.ComponentModel;
namespace ConsoleApplication1
{
public enum ThreadPriority
{
[Description("Highest")]
Highest,
[Description("Above Normal")]
AboveNormal,
[Description("Normal")]
Normal,
[Description("Below Normal")]
BelowNormal,
[Description("Lowest")]
Lowest
}
class Program
{
static void Main(string[] args)
{
ThreadPriority threadPriority = EnumHelper<ThreadPriority>.Parse("AboveNormal");
System.Diagnostics.Debug.Assert(threadPriority == ThreadPriority.AboveNormal);
string description = EnumHelper<ThreadPriority>.EnumValueDescription(threadPriority);
System.Diagnostics.Debug.Assert(description.Equals("Above Normal"));
threadPriority = EnumHelper<ThreadPriority>.ParseOrDescriptionMatch("Below Normal", ThreadPriority.Normal);
System.Diagnostics.Debug.Assert(threadPriority == ThreadPriority.BelowNormal);
if (!EnumHelper<ThreadPriority>.TryParse("Highest", out threadPriority))
{
System.Diagnostics.Debug.Fail("TryParse expected to succeed");
}
System.Diagnostics.Debug.Assert(threadPriority == ThreadPriority.Highest);
if (EnumHelper<ThreadPriority>.TryParse("Foo Bar", out threadPriority))
{
System.Diagnostics.Debug.Fail("TryParse expected to fail");
}
}
}
public static class EnumHelper<T>
where T : struct, IComparable, IFormattable, IConvertible
{
///
/// Static constructor to ensure T is an enum
///
static EnumHelper()
{
if (!typeof(T).IsEnum)
{
throw new ArgumentException("Type parameter must be an enum");
}
}
/// <summary>
/// Converts the string representation of the name or numeric value of one or
/// more enumerated constants to an equivalent enumerated object.
/// </summary>
/// <param name="value">A string containing the name or value to convert.</param>
/// <returns>An object of type T whose value is represented by value.</returns>
/// <exception cref="System.ArgumentNullException">value is null</exception>
/// <exception cref="System.ArgumentException">value is either an empty string or
/// only contains white space. -or- value is a name, but not one of the named
/// constants defined for the enumeration.
///</exception>
public static T Parse(string value)
{
return (T)Enum.Parse(typeof(T), value);
}
///
/// Converts the string representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.
///
/// A string containing the name or value to convert.
/// When this method returns, contains the object of type T whose
/// value is represented by the value, if the conversion succeeded, or the first
/// value in the enum if conversion failed. This parameter is passed uninitialized.
/// true if value was converted successfully; otherwise, false.
public static bool TryParse(object value, out T returnValue)
{
Type underlyingType = Enum.GetUnderlyingType(typeof(T));
bool supportedType = (value is string || value.GetType().Equals(underlyingType));
if (supportedType && Enum.IsDefined(typeof(T), value))
{
//direct string or underlying type match
returnValue = Parse(value.ToString());
return true;
}
else if (Enum.IsDefined(typeof(T), Convert.ChangeType(value, underlyingType))) // May throw overflow exception. E.g. long.MaxValue to Int32
{
//underlying numeric type match after type conversion
returnValue = (T)Enum.Parse(typeof(T), value.ToString());
return true;
}
else
{
//Default to the first item from the enum
string[] values = Enum.GetNames(typeof(T));
returnValue = (T)Enum.Parse(typeof(T), values[0], true);
//default(T) won't work for all enums.
//E.g. if the underlying type for the enum is int default(T) will always return 0, for which there might not be a value.
}
return false;
}
/// <summary>
/// Attempt to read the Description Attribute
/// </summary>
/// <param name="e">The enum value to read the description from</param>
/// <returns>The description value for the enum, otherwise the enum value converted to a string.</returns>
public static string EnumValueDescription(T e)
{
System.Reflection.FieldInfo EnumInfo = e.GetType().GetField(e.ToString());
System.ComponentModel.DescriptionAttribute[] enumAttributes =
(System.ComponentModel.DescriptionAttribute[])
EnumInfo.GetCustomAttributes(typeof(System.ComponentModel.DescriptionAttribute), false);
if (enumAttributes.Length > 0)
{
return enumAttributes[0].Description;
}
return e.ToString();
}
/// <summary>
/// Converts the string representation of the name or numeric value of one or
/// more enumerated constants to an equivalent enumerated object.
/// If a direct match isn't found a match will be attempted on the description attributes.
/// </summary>
/// <param name="value"></param>
/// <param name="defaultValue"></param>
/// <returns></returns>
public static T ParseOrDescriptionMatch(string value, T defaultValue)
{
Type type = typeof(T);
try
{
return Parse(value);
}
catch (Exception)
{
//Try Description Matching
string[] names = Enum.GetNames(type);
foreach (string name in names)
{
T nameEnum = Parse(name);
string nameEnumValue = EnumValueDescription(nameEnum);
if (nameEnumValue == value)
{
return nameEnum;
}
}
return defaultValue;
}
}
}
}
Friday, February 13, 2009
EnumHelper using generics to reduce casting
Thursday, February 12, 2009
Common Visual Studio settings
Tools > Options > Projects and Solutions > General > ...
- "Track Active Item in Solution Explorer"
- "Show Output window when build starts"
Tools > Options > Environment > Find and Replace ...
- Automatically populate Find What with text from the editor
If you have a suitable level of OCD you can also show white spaces.
Edit > Advanced > View White Space. See Also Coding Horror - Whitespace: The Silent Killer
DataReader.ToInt32(0) versus Convert.ToInt32(dataReader[0])
DataReader.GetInt32() will not perform any type conversions; therefore, the data retrieved must already be a 32-bit signed integer. Convert.ToInt32() will be happy to convert from a double, for example.
I'd tend to err towards the stricter approach to avoid unforeseen casting issues. I.e. something changes in the database.
Checks for IsDBNull() may also be required.
You will also need to consider how to reference the columns. By name or by ordinal position. See Roughly 3% penalty for indexing SqlDataReader columns by string rather than int.
Monday, February 9, 2009
Fine grained authorization in ASP.NET 2.0+
I have the requirement for fine grained authorization control. I considered using the standard ASP.NET roles, but really need an extra "level" added.
For example, tasks or logical operations like "CanCreateNewDocument" map to logical roles in the organization like "Manager". Users are then assigned to logical roles rather than logical operations. Authorization checks are performed against logical operations.
There doesn't appear to be the ability to nest roles within roles. I.e. Role Inheritance.
Something like the Microsoft Authorization Manager may be able to fill the gap.
Using Membership, Role, and Profile outside of ASP.NET (3.5)
LINQ to SQL and LINQ to Entities
Generating compiled help files - CHM with sandcastle
Sandcastle, created by Microsoft, is a tool used for creating MSDN-style documentation from .NET assemblies and their associated XML comments files. The current version is the May 2008 release. It is command line based and has no GUI front-end, project management features, or an automated build process like those that you can find in NDoc. The Sandcastle Help File Builder was created to fill in the gaps, provide the missing NDoc-like features that are used most often, and provide graphical and command line based tools to build a help file in an automated fashion.
See also - MSDN: Recommended Tags for Documentation Comments (C# Programming Guide)