> 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, string> dictLanguage;
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, string> dictPostfix = 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
David Jackson - 29 Jun 2007 12:59 GMT
Hi Peter,
Thanks for the reply.
> 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).
OK.
> 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):
Yes, I can see that.
> Dictionary<string, Dictionary<int, string>> _dict = new Dictionary<string,
> Dictionary<int, string>>();
FxCop tells me that it doesn't recommend nested generics - are they really
OK?
> Hope that helps, rather than confuses things further. :)
I found it very useful. Thanks again.
DJ
Mark Rae - 29 Jun 2007 14:37 GMT
>> Dictionary<string, Dictionary<int, string>> _dict = new
>> Dictionary<string, Dictionary<int, string>>();
> FxCop tells me that it doesn't recommend nested generics - are they really
> OK?
FxCop's recommendations really should taken very much with a pinch of
salt...
Peter's code looks fine to me, and I would have no qualms about using
"nested generics"...
In fact, until I saw your post, the concept of a "nested generic" never
crossed my mind...

Signature
http://www.markrae.net
Peter Duniho - 29 Jun 2007 17:26 GMT
> [...]
> FxCop tells me that it doesn't recommend nested generics - are they
> really OK?
I don't know anything about FxCop. Sounds a little fussy to me. :)
I have no idea why it might suggest that nested generics should be
avoided. That's like saying one should avoid putting references to any
class within any other class. But, it should go without saying, since I
wrote the code I at least thought it was okay at the time I wrote it. :)
Pete
Rather than designing and implementing this from scratch, why not look at
one of the Perl or Ruby library functions that already accomplish this, and
convert it to C#?
///ark