|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
ContentsIntroductionWhat? Yet another argument parsing class?! What for? Well, actually for a couple of reasons:
I want to thank Ray Hayes for his idea of automatically setting field/property by judiciously using custom attributes. This is a great example of that proverbial 1% of inspiration. :) Take a look at his article here. Quick ExampleThis is an example quickly showing how you might use the class, more or less taken from the included demo, which by the way is a utility I posted some time ago on this site. You can read the article here. using Common;
using Common.IO;
using System;
using System.Collections;
using System.Collections.Specialized;
using System.IO;
namespace xmove
{
class XMove
{
private static System.Resources.ResourceManager m_resMan;
// this argument can either be named "batch", "file", "batchFile"
// or even "smurf" if you fancy it
[AutoSetMember("batch", "file", "batchFile", "smurf")]
private static String m_batchFile;
[AutoSetMember("yc", SwitchMeansFalse=true)]
private static bool m_confirmMove = true;
[AutoSetMember("yc", SwitchMeansFalse=true)]
private static bool m_confirmOverwrite = true;
[AutoSetMember("r")]
private static bool m_recursive = false;
private static FindFile.SearchAttributes m_searchAttributes;
[STAThread]
static void Main(string[] args)
{
// a FlagCollection contains all accepted flags
FlagCollection flags = new FlagCollection();
// here we add the flag named "a" with accepted values
// a,c,d,h,...
// the argument must then looks like "-a<VALUES>" on the command
// line
flags.Add("a", "acdehnorstFA");
// create a new parser that accept all argument formats,
// is case sensitive and accept provided flags
ArgumentParser parser = new ArgumentParser(ArgumentFormats.All,
false, flags);
// parse the arguments with the result stored in a
// StringDictionary
StringDictionary theArgs = parser.Parse(args);
// automatically set all static members of the class XMove
parser.AutoSetMembers(typeof(XMove));
// now, the values of m_batchFile, m_confirmMove,
// m_confirmOverwrite and m_recursive are all set !
// the corresponding arguments have been moved to
// parser.HandledArguments
// the remaining arguments are in parser.UnhandledArguments
// one argument remains which can't be set automatically
if (theArgs.ContainsKey("a"))
{
String attribs = theArgs[m_resMan.GetString("app0019")];
if (attribs.IndexOf("A") > -1)
m_searchAttributes |= FindFile.SearchAttributes.All;
if (attribs.IndexOf("F") > -1)
m_searchAttributes |= FindFile.SearchAttributes.AnyFile;
.........
}
// here we load the resources for the application
LoadResources();
// we then set the resource manager of the
// AutoSetMemberAttribute accordingly
// why this property is static is explained in the
// "How it Works" section of this article
AutoSetMemberAttribute.Resources = m_resMan;
// create a new instance of Dummy class
Dummy dummy = new Dummy();
// then set members of this class, no matter if they are
// static, instance, public, private, protected, internal
// type conversion is done automatically !
parser.AutoSetMembers(dummy);
}
private void LoadResources()
{
....
}
}
class Dummy
{
public enum MyEnum
{
a,
b,
c,
d
}
[AutoSetMember("foo")]
private static int field1;
// here we indicate that the argument name is to be retrieved using
// the ResourceManager at runtime
[AutoSetMember(ResID="0001")]
private Double field2;
// same here
[AutoSetMember(ResID="0002")]
protected String Property1
{
get {...}
set {...}
}
[AutoSetMember("buzz")]
public MyEnum Property2
{
get {...}
set {...}
}
}
}
Using the codeTwo main classes do all the work: ArgumentParser classProperties
Methods
AutoSetMemberAttributeProperties
How it WorksParsing of the command lineHere is a quick explanation of the regex that is constructed by // The whole parsing string (with all possible argument formats) :
// ---------------------------------------------------------------
// (CUSTOM_PATTERN)
// |(^(?
Automatically setting member valuesAt design time, At runtime, you can set the When you execute
/// <SUMMARY>
/// Automatically sets members for the provided
/// <SEE cref="System.Reflection.Assembly" />.
/// </SUMMARY>
/// The <SEE cref="System.Reflection.Assembly" /> to process.
public void AutoSetMembers(Assembly assembly)
{
Type[] types = assembly.GetTypes();
foreach (Type type in types)
AutoSetMembers(type);
}
/// <SUMMARY>
/// Automatically sets members for the provided <SEE cref="System.Type" />.
/// </SUMMARY>
/// The <SEE cref="System.Type" /> to process.
/// <REMARKS>Only static members will be processed.</REMARKS>
public void AutoSetMembers(Type type)
{
MemberInfo[] members = type.FindMembers(
AutoSetMemberAttribute.SupportedMemberTypes,
AutoSetMemberAttribute.SupportedBindingFlags, Type.FilterName, "*");
foreach (MemberInfo member in members)
AutoSetMembers(type, member);
}
/// <SUMMARY>
/// Automatically sets members for the provided class instance.
/// </SUMMARY>
/// The class instance to process. Must not be null.
/// <REMARKS>Both static and instance members will be processed.</REMARKS>
public void AutoSetMembers(object instance)
{
MemberInfo[] members = instance.GetType().FindMembers(
AutoSetMemberAttribute.SupportedMemberTypes,
AutoSetMemberAttribute.SupportedBindingFlags, Type.FilterName, "*");
foreach (MemberInfo member in members)
AutoSetMembers(instance, member);
}
/// <SUMMARY>
/// Automatically sets member of the provided class instance or
/// <SEE cref="System.Type" />.
/// </SUMMARY>
/// The class instance or <SEE cref="System.Type" /> to process.
/// The member which will be set. Must be a field or a property.
/// <REMARKS>Both static and instance members are accepted.</REMARKS>
public void AutoSetMembers(object classToProcess, MemberInfo member)
{
AutoSetMemberAttribute attrib = Attribute.GetCustomAttribute(member,
typeof(AutoSetMemberAttribute)) as AutoSetMemberAttribute;
if (attrib != null)
{
if (attrib.ResID != null && AutoSetMemberAttribute.Resources != null)
attrib.Aliases.Add(AutoSetMemberAttribute.Resources.GetString(
attrib.ResID, AutoSetMemberAttribute.Culture));
String argValue = null;
bool found = false;
foreach (String alias in attrib.Aliases)
{
if (m_unhandled.ContainsKey(alias))
{
argValue = (String)m_unhandled[alias];
m_handled.Add(alias, argValue);
m_unhandled.Remove(alias);
found = true;
break;
}
else if (m_handled.ContainsKey(alias))
{
argValue = (String)m_handled[alias];
found = true;
break;
}
}
if (found)
{
Type memberType = null;
switch (member.MemberType)
{
case MemberTypes.Property:
memberType = ((PropertyInfo)member).PropertyType;
break;
case MemberTypes.Field:
memberType = ((FieldInfo)member).FieldType;
break;
}
if (memberType == typeof(bool))
{
if (argValue == "")
SetMemberValue(classToProcess, member,
!attrib.SwitchMeansFalse);
else if (argValue == Boolean.FalseString ||
argValue == Boolean.TrueString)
SetMemberValue(classToProcess, member,
Boolean.Parse(argValue));
else
// last chance ... if can't parse it as integer, an
// exception will be raised
SetMemberValue(classToProcess, member,
Int32.Parse(argValue) != 0);
}
else if (memberType == typeof(String))
SetMemberValue(classToProcess, member, argValue);
else if (memberType.IsEnum)
{
object value = Enum.Parse(memberType, argValue,
m_ignoreCase);
SetMemberValue(classToProcess, member, value);
}
else if (memberType.IsValueType)
SetMemberValue(classToProcess, member,
Convert.ChangeType(argValue, memberType));
}
}
}
/// <SUMMARY>
/// Sets the static or instance member (property or field) to the specified
/// value.
/// </SUMMARY>
/// The class instance or <SEE cref="System.Type" /> to be used.
/// The member to be set.
/// The new value of the member.
private void SetMemberValue(object instance, MemberInfo memberInfo,
object value)
{
if (memberInfo is PropertyInfo)
{
PropertyInfo pi = (PropertyInfo) memberInfo;
if (pi.CanWrite)
{
MethodInfo methodInfo = pi.GetSetMethod(true);
BindingFlags bindingFlags = BindingFlags.SetProperty;
if (methodInfo.IsStatic)
bindingFlags |= BindingFlags.Static;
pi.SetValue(instance, value, bindingFlags, null, null, null);
}
}
else if (memberInfo is FieldInfo)
{
FieldInfo fi = (FieldInfo) memberInfo;
BindingFlags bindingFlags = BindingFlags.SetField;
if (fi.IsStatic)
bindingFlags |= BindingFlags.Static;
fi.SetValue(instance, value, bindingFlags, null, null);
}
}
|
||||||||||||||||||||||