On Thu, 28 Jun 2007 14:20:42 -0700, David Jackson <so*****@somewh ere.com
wrote:
OK, now this is my first attempt at showing any code in public, so I
would
really appreciate any and all criticism. I'm still very much a newbie
compared to most who post in here.
Because of your desire to make it extensible, I would use the Dictionary<
class to handle the actual look-up (which is mostly what your function
does).
For example (you will note that, counting initialization, this code is
longer than the code you posted, but as new languages are added, two
things are true: 1) this code stays the same length, as code based on your
approach increases proportionally to the data being added, and 2) once
this code is shown to be correct, adding new data is simple and less
error-prone):
(Warning: I haven't compiled any of this code. There may well be some
fundamental syntax errors, like you need to cast something I forgot to
cast. However, I am confident that the basic design is fine, so don't let
any little thing like compiler errors dissuade you. :) )
Dictionary<stri ng, Dictionary<int, string>_dict = new Dictionary<stri ng,
Dictionary<int, string>>();
static string OrdinalDate(Dat eTime DateToFormat, string MonthFormat, string
YearFormat, CultureInfo CI)
{
StringBuilder SB = new StringBuilder() ;
Dictionary<int, stringdictLangu age;
SB.Append(DateT oFormat.Day.ToS tring("d"));
try
{
string strPostfix;
dictLanguage = _dict[CI.TwoLetterISO LanguageName].Value;
try
{
strPostfix = dictLanguage[DateToFormat.Da y].Value;
}
catch (KeyNotFoundExc eption)
{
// The dictionary must always include key 0, which is the
default
// to use if the actual date number isn't found.
strPostfix = dictLanguage[0].Value;
}
SB.Append(strPo stfix);
}
catch (KeyNotFoundExc eption exc)
{
throw new NotSupportedExc eption("Unknown language in CultureInfo
CI", exc);
}
SB.Append(" ");
SB.Append(DateT oFormat.ToStrin g(MonthFormat, CI.DateTimeForm at));
SB.Append(" ");
SB.Append(DateT oFormat.ToStrin g(YearFormat, CI.DateTimeForm at));
return SB.ToString();
}
The idea being that the _dict field would be initialized to contain
dictionaries for each language you support, each dictionary containing a
default string to append (using index 0) and then specific strings for
specific values. You could of course write explicit code to initialize
the Dictionary<inst ances, but in keeping with the data-driven model, I
would do something like this:
void InitOrdinalDate ()
{
object[] objsDictInit = new object[] {
new object[]
{ "en",
new object[] { "th", 0 },
new object[] { "st", 1, 21, 31 },
new object[] { "nd", 2, 22 },
new object[] { "rd", 3, 23 }
},
new object []
{ "fr",
new object[] { "e", 0 },
new object[] { "er", 1 }
} };
Dictionary<stri ng, Dictionary<int, string>dictLang = new
Dictionary<stri ng, Dictionary<int, string>>();
foreach (object[] arrayLang in objsDictInit)
{
string strLanguage = arrayLang[0];
Dictionary<int, stringdictPostf ix = new Dictionary<int,
string>();
for (int i = 1; i < arrayLang.Lengt h; i++)
{
object[] arrayPostfix = arrayLang[i];
string strPostfix = arrayPostfix[0];
for (int j = 1; j < arrayPostfix.Le ngth; j++)
{
dictPostfix.Add (arrayPostfix[j], strPostfix);
}
}
dictLang.Add(st rLanguage, dictPostfix);
}
_dict = dictLang;
}
You'd run this code once, and then the OrdinalDate() method would simply
reuse the results each time you call that method. Note also that the
awkward "arrays of arrays of arrays" design can easily be replaced with
something that just reads an XML file or string. IMHO, that would
actually be a better approach than what I posted, but I wanted to get the
illustration out without complicating things further. While I think XML
would be more maintainable, the above has the advantage that is uses only
the basic built-in stuff.
Hope that helps, rather than confuses things further. :)
Pete