On Thu, 28 Jun 2007 14:20:42 -0700, David Jackson <so*****@somewhere.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<string, Dictionary<int, string>_dict = new Dictionary<string,
Dictionary<int, string>>();
static string OrdinalDate(DateTime DateToFormat, string MonthFormat, string
YearFormat, CultureInfo CI)
{
StringBuilder SB = new StringBuilder();
Dictionary<int, stringdictLanguage;
SB.Append(DateToFormat.Day.ToString("d"));
try
{
string strPostfix;
dictLanguage = _dict[CI.TwoLetterISOLanguageName].Value;
try
{
strPostfix = dictLanguage[DateToFormat.Day].Value;
}
catch (KeyNotFoundException)
{
// 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(strPostfix);
}
catch (KeyNotFoundException exc)
{
throw new NotSupportedException("Unknown language in CultureInfo
CI", exc);
}
SB.Append(" ");
SB.Append(DateToFormat.ToString(MonthFormat, CI.DateTimeFormat));
SB.Append(" ");
SB.Append(DateToFormat.ToString(YearFormat, CI.DateTimeFormat));
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<instances, 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<string, Dictionary<int, string>dictLang = new
Dictionary<string, Dictionary<int, string>>();
foreach (object[] arrayLang in objsDictInit)
{
string strLanguage = arrayLang[0];
Dictionary<int, stringdictPostfix = new Dictionary<int,
string>();
for (int i = 1; i < arrayLang.Length; i++)
{
object[] arrayPostfix = arrayLang[i];
string strPostfix = arrayPostfix[0];
for (int j = 1; j < arrayPostfix.Length; j++)
{
dictPostfix.Add(arrayPostfix[j], strPostfix);
}
}
dictLang.Add(strLanguage, 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