ICustomFormatter and the ToString( IFormatProvider ) method of Enum
17
Aug
I’m having real problems today with some C# code for a an ICustomFormatter … it works fine if I call String.Format(...) but fails miserably if I use .ToString( ... ) and I can’t figure out why. I’ll paste the code in here, and continue the discussion below it.
namespace RMST.
Utilities {
using System;
using System.
Reflection;
#region NUnit Tests
using NUnit.
Framework;
[TestFixture
]
public class EnumTest
{
// Just a simple sample enumeration
public enum Currencies
{
[Text
("US Dollars")]
USD,
[Text
("British Pounds")]
GBP,
[Text
("Costa Rican Colones")]
CRC,
[Text
("European Dollar")]
EUD
}
[Test
]
public void SimpleTest
() {
Assert.
AreEqual( "US Dollars", Currencies.
USD.
ToString( new TAFormat
() ) );
Assert.
AreEqual( "US Dollars",
String.
Format(TAFormat.
Formatter,
"{0}", Currencies.
USD ) );
}
}
#endregion
// We create a "Text" attribute
public class TextAttribute : Attribute
{
public readonly string Text;
public TextAttribute
( string text
) {
Text = text;
}
}
public class TAFormat : IFormatProvider, ICustomFormatter
{
public static TAFormat Formatter =
new TAFormat
();
static string GetEnumText
( Enum arg
) {
// get the item we want from the list of the members of the enum
Type enumType = arg.
GetType();
MemberInfo
[] members = enumType.
GetMember(arg.
ToString());
// if we found it, get it's text attribute, or return null
if( members !=
null && members.
Length >
0 ) {
object[] attribs = members
[0].
GetCustomAttributes(typeof(TextAttribute
),
false);
if( attribs.
Length >
0 ) {
return ((TextAttribute
)attribs
[0]).
Text;
}
}
return null;
}
static object GetEnumValue
(string text, Type enumType
) {
// get a list of the members of the enum
MemberInfo
[] members = enumType.
GetMembers();
// iterate through checking the TextAttribute until we find a match
foreach( MemberInfo mi
in members
) {
object[] attrs = mi.
GetCustomAttributes(typeof(TextAttribute
),
false);
if( attrs.
Length ==
1 &&
((TextAttribute
)attrs
[0]).
Text == text
) {
return Enum.
Parse(enumType, mi.
Name);
}
}
throw new ArgumentOutOfRangeException
("text", text,
"The text passed does not correspond to an attributed enum value");
}
#region IFormatProvider Members
// String.Format calls this method to get an instance of an
// ICustomFormatter to handle the formatting.
public object IFormatProvider.
GetFormat (Type service
) {
if (service ==
typeof (ICustomFormatter
)) {
return this;
}
else {
return null;
}
}
#endregion
#region ICustomFormatter Members
// After String.Format gets the ICustomFormatter, it calls
// this format method on each argument.
public string ICustomFormatter.
Format (string format,
object arg, IFormatProvider provider
) {
string formattedString =
null;
if( null == arg
) {
throw new ArgumentNullException
();
}
// Custom Formatting
if( arg
is Enum &&
(format ==
null || format.
Equals("T"))) {
formattedString = GetEnumText
( arg
as Enum );
}
// Default Formatting
if( null == formattedString
) {
DefaultHandler
( format, arg, provider
);
}
return formattedString;
}
private string DefaultHandler
(string format,
object arg, IFormatProvider provider
) {
// Default handling ... wish there was a SystemDefaultFormatter.Format( ... )
if (arg
is IFormattable
) {
formattedString =
((IFormattable
)arg
).
ToString( format, formatProvider
);
} else { // if arg != null, but we already tested for that
formattedString = arg.
ToString();
}
}
#endregion
}
}
Hopefully you’re able to see what I’m trying to do here. Basically, I’m creating an attribute-based custom string conversion for Enum types. The idea is that each member of the Enum gets a TextAttribute which specifies how it should be converted to text. The EnumStringFormat custom formatter checks for that attribute and uses it, or else uses the default method (which returns the “Name” of the enum member). As a side note my “Currencies” example test code is just that: an example. It has nothign to do with the Currency type, it’s merely an example enumeration I thought would be simple to grasp.
Anyway, if you put that code into a C# file and compile it and run that test through NUnit, you will see that String.Format(TAFormat.Formatter, "{0}", Currencies.USD ) returns “US Dollars” but Currencies.USD.ToString( TAFormat.Formatter ) returns “USD” (the Name).
If anyone has any insight on this, I’d love to hear it. The whole point of the excercise was supposed to be to simplify the attribute trick to just passing the formatter (as opposed to some other examples which required calling a static member of a custom class).
- By Joel 'Jaykul' Bennett
- Tagged as: