473,238 Members | 1,940 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,238 software developers and data experts.

Generic methods and Switch by type

Hi,
I have written a generic method which does different things depending on
the type of the parameter. I got it to work, but it seems really inelegant.
Is there a better way to do this?
In the code below, ColumnMap is a simple struct which basically has three
properties, Header (a string), Index (an int), TypeOfData (which is a
DataType which is a local eNum). _Mapping is a local List<ColumnMap>.
public ColumnMap GetMap<T>(T value)
{
Type ThisType = typeof(T);
foreach (ColumnMap ThisMap in _Mapping)
{
if (ThisType == typeof(string))
{
if (ThisMap.Header.ToUpper() ==
value.ToString().ToUpper())
{
return ThisMap;
}
}
if (ThisType == typeof(int))
{
if (ThisMap.Index == int.Parse(value.ToString()))
{
return ThisMap;
}

}
if (ThisType == typeof(DataType))
{
if (ThisMap.TypeOfData.ToString() == value.ToString())
{
return ThisMap;
}
}
}
return new ColumnMap();
}
}
I would have prefered to do my comparisons by casting value to string, int,
ot DataType, but that gave me "cannot convert Type 'T' to ____" errors when
compiling.

I know could have written this as three overloaded methods with different
signatures, but I thought using a single Generic method would be easier.

Thanks for any thoughts!
Ethan

Ethan Strauss Ph.D.
Bioinformatics Scientist
Promega Corporation
2800 Woods Hollow Rd.
Madison, WI 53711
608-274-4330
800-356-9526
et***********@promega.com

Jun 27 '08 #1
4 7775
Ethan Strauss wrote:
Hi,
I have written a generic method which does different things depending on
the type of the parameter. I got it to work, but it seems really inelegant.
Is there a better way to do this?
In the code below, ColumnMap is a simple struct which basically has three
properties, Header (a string), Index (an int), TypeOfData (which is a
DataType which is a local eNum). _Mapping is a local List<ColumnMap>.
public ColumnMap GetMap<T>(T value)
{
Type ThisType = typeof(T);
foreach (ColumnMap ThisMap in _Mapping)
{
if (ThisType == typeof(string))
{
if (ThisMap.Header.ToUpper() ==
value.ToString().ToUpper())
Don't do this, use one of the overloads of String.Equals() instead.
{
return ThisMap;
}
}
if (ThisType == typeof(int))
{
if (ThisMap.Index == int.Parse(value.ToString()))
{
return ThisMap;
}

}
if (ThisType == typeof(DataType))
{
if (ThisMap.TypeOfData.ToString() == value.ToString())
{
return ThisMap;
}
}
}
return new ColumnMap();
}
}
I would have prefered to do my comparisons by casting value to string, int,
ot DataType, but that gave me "cannot convert Type 'T' to ____" errors when
compiling.
Generics are supposed to promote type safety. Casting them to arbitrary
types makes no sense. You would actually have been better off if you'd
simply let this function take an Object.
I know could have written this as three overloaded methods with different
signatures, but I thought using a single Generic method would be easier.
When you found out you were wrong, why didn't you reverse your decision?
They really *are* three separate methods, and "type" is not sufficient to
distinguish them: you're not looking up a ColumnMap "by string", you're
looking it up "by header". You're not looking it up "by int", you're looking
it up "by index". So make the methods reflect that (this uses C# 3.0):

public ColumnMap GetMapByHeader(string header) {
return _Mapping.Where(m =string.Equals(m.Header, header,
StringComparison.InvariantCultureIgnoreCase)).Firs tOrDefault();
}

public ColumnMap GetMapByIndex(int index) {
return _Mapping.Where(m =m.Index == index).FirstOrDefault();
}

public ColumnMap GetMapByTypeOfData(DataType dataType) {
return _Mapping.Where(m =m.TypeOfData == dataType).FirstOrDefault();
}

If you want to you can name these all the same; I wouldn't, but it's a
personal taste.

Note also that it's poor form to make a function that's nominally intended
to get an existing item create a new one when it can't find the value
supplied. Are clients supposed to check for existence first if they want to
find out if the value is present at all, or do default ColumnMaps have some
sort of .IsActuallyNotValid property?

Finally, clients will presumably know what ColumnMaps they want to find
themselves. Instead of offering separate methods for searching for them,
offer them access to the collection itself:

public IList<ColumnMapMappings {
get { return _Mappings.AsReadOnly(); }
}

This property can be queried in any way the client wants. If you do want to
offer specialized lookup, consider creating a new ColumnMappingCollection
class instead.

--
J.
http://symbolsprose.blogspot.com
Jun 27 '08 #2
Thanks for you reply Jeroen. I have some comments on your comments
Ethan
"Jeroen Mostert" wrote:
Ethan Strauss wrote:
Hi,
I have written a generic method which does different things depending on
the type of the parameter. I got it to work, but it seems really inelegant.
Is there a better way to do this?
In the code below, ColumnMap is a simple struct which basically has three
properties, Header (a string), Index (an int), TypeOfData (which is a
DataType which is a local eNum). _Mapping is a local List<ColumnMap>.
public ColumnMap GetMap<T>(T value)
{
Type ThisType = typeof(T);
foreach (ColumnMap ThisMap in _Mapping)
{
if (ThisType == typeof(string))
{
if (ThisMap.Header.ToUpper() ==
value.ToString().ToUpper())

Don't do this, use one of the overloads of String.Equals() instead.
Why not? Not that I object to String.Equals(), but I would think that the
amount of extra work the CPU has to do would be pretty trivial. Am I wrong?
>
{
return ThisMap;
}
}
if (ThisType == typeof(int))
{
if (ThisMap.Index == int.Parse(value.ToString()))
{
return ThisMap;
}

}
if (ThisType == typeof(DataType))
{
if (ThisMap.TypeOfData.ToString() == value.ToString())
{
return ThisMap;
}
}
}
return new ColumnMap();
}
}
I would have prefered to do my comparisons by casting value to string, int,
ot DataType, but that gave me "cannot convert Type 'T' to ____" errors when
compiling.
Generics are supposed to promote type safety. Casting them to arbitrary
types makes no sense. You would actually have been better off if you'd
simply let this function take an Object.
Not a bad idea. I may do that....
>
I know could have written this as three overloaded methods with different
signatures, but I thought using a single Generic method would be easier.
When you found out you were wrong, why didn't you reverse your decision?
They really *are* three separate methods, and "type" is not sufficient to
distinguish them:
You are part of the process of deciding what to do now. Going to 3 seperate
methods may be what I do, but I wanted to see if I was missing something
first.
Note also that it's poor form to make a function that's nominally intended
to get an existing item create a new one when it can't find the value
supplied. Are clients supposed to check for existence first if they want to
find out if the value is present at all, or do default ColumnMaps have some
sort of .IsActuallyNotValid property?
I do expect that clients will check for existence first, but if I leave this
out, then I get a "not all paths return a value" type error. Would you prefer
throwing an error here? If so, why?
>
Finally, clients will presumably know what ColumnMaps they want to find
themselves. Instead of offering separate methods for searching for them,
offer them access to the collection itself:

public IList<ColumnMapMappings {
get { return _Mappings.AsReadOnly(); }
}

This property can be queried in any way the client wants. If you do want to
offer specialized lookup, consider creating a new ColumnMappingCollection
class instead.
I have considered this and I think it is probably the way to go. I will
think some more on it and see what I come up with


Ethan
Jun 27 '08 #3
Ethan Strauss wrote:
"Jeroen Mostert" wrote:
>Ethan Strauss wrote:
>>Hi,
I have written a generic method which does different things depending on
the type of the parameter. I got it to work, but it seems really inelegant.
Is there a better way to do this?
In the code below, ColumnMap is a simple struct which basically has three
properties, Header (a string), Index (an int), TypeOfData (which is a
DataType which is a local eNum). _Mapping is a local List<ColumnMap>.
public ColumnMap GetMap<T>(T value)
{
Type ThisType = typeof(T);
foreach (ColumnMap ThisMap in _Mapping)
{
if (ThisType == typeof(string))
{
if (ThisMap.Header.ToUpper() ==
value.ToString().ToUpper())
Don't do this, use one of the overloads of String.Equals() instead.
Why not? Not that I object to String.Equals(), but I would think that the
amount of extra work the CPU has to do would be pretty trivial. Am I wrong?
The problem is that if you use .ToUpper(), it's not clear what sort of
comparison you're intending. The default comparison is culture-specific,
which is rarely appropriate and leads to such classical problems as the one that

"i".ToUpper() == "I".ToUpper()

is *false* when the locale is Turkish (the so-called "Turkish i problem"),
because Turkish has two distinct letter i's (with and without dot).
String.Equals() avoids these issues. So do .ToUpper() with an appropriate
argument and .ToUpperInvariant(), but if you have the opportunity to do an
efficient comparison instead of producing more strings, why waste it? It's
still one line of code.
>Note also that it's poor form to make a function that's nominally intended
to get an existing item create a new one when it can't find the value
supplied. Are clients supposed to check for existence first if they want to
find out if the value is present at all, or do default ColumnMaps have some
sort of .IsActuallyNotValid property?

I do expect that clients will check for existence first, but if I leave this
out, then I get a "not all paths return a value" type error. Would you prefer
throwing an error here? If so, why?
There are plenty of patterns here:

- Provide GetValue() and have it return null if the specified value isn't
there. For this to work, null must not be a valid item in the collection.
This is by far the easiest approach. Clients have little opportunity for
failing by not checking the return value, because any call on the resulting
object will throw a NullReferenceException. It's also easy to propagate null
values to callers upstream that use the same convention.

- As a refinement of this, you can return a "null object" instead. This
object is an actual object (not a null reference) which simply provides a
do-nothing or trivial implementations of every method. This can remove
checking of edge cases and simplify logic in some cases, but the downside is
that it complicates handling for clients that are only interested in "real"
objects. Also, some methods may simply not allow for a meaningful null
implementation.

- Provide HasValue() and GetValue() and have GetValue() throw an exception.
This works, but it means getting a value if you don't know it exists is
twice as expensive (GetValue() will presumably search for the value in
exactly the same way HasValue() does). It also means that if this collection
is used in a concurrent scenario, clients *must* lock on the collection for
every single access, because there's no atomic way of retrieving a value
successfully.

- Provide GetValue(), which throws an exception if the item is not there,
and bool TryGetValue(out value), which sets value and returns a boolean
indicating whether the item was found. This is the generalization of
solution #1, used by (among others) Dictionary<T>. This works even if null
is an item in the collection, but TryGetValue() is cumbersome to call, and
this can get annoying especially if the common case is *not* knowing whether
an item is present.

Which one to use depends on your scenario. There's another approach that,
while often seen, I would rarely consider useful:

- Provide only GetValue() and have it throw an exception for values that
aren't there. This is only appropriate if clients *must* know a value exists
and any failure to know this is to be considered a bug, because "exceptions
should be exceptional". The question to ask here is: how can clients
*definitely know* the item is there unless they know someone else who put it
there? If they do, why didn't that party simply pass them the item instead
of making them retrieve it?

--
J.
http://symbolsprose.blogspot.com
Jun 27 '08 #4
Ethan Strauss <Et**********@discussions.microsoft.comwrote:
Don't do this, use one of the overloads of String.Equals() instead.
Why not? Not that I object to String.Equals(), but I would think that the
amount of extra work the CPU has to do would be pretty trivial. Am I wrong?
Yes. Try comparing "MAIL" and "mail" with your code, but when you're
running in a Turkish locale.

If you want a case-insensitive comparison, do one - upper casing and
then doing an ordinal comparison is inherently culture-sensitive, and
can give inappropriate results.

(It also avoids creating extra strings for no good reason.)

--
Jon Skeet - <sk***@pobox.com>
Web site: http://www.pobox.com/~skeet
Blog: http://www.msmvps.com/jon.skeet
C# in Depth: http://csharpindepth.com
Jun 27 '08 #5

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

3
by: Jim Newton | last post by:
hi all, i'm relatively new to python. I find it a pretty interesting language but also somewhat limiting compared to lisp. I notice that the language does provide a few lispy type nicities, but...
6
by: Urs Eichmann | last post by:
While experimenting with the Feb CTP Edition of VB 2005, I came across "generic procedures". You can write: Public Class Foo Public Sub MySub(Of tDisp As IDisposable)(ByVal vMyParm As Integer)...
10
by: steve bull | last post by:
I have a class SwatchPanel which takes Swatch as a parameter type. How can I call a static function within the Swatch class? For example the code below fails on TSwatch.Exists. How can I get the...
3
by: Scottie_do | last post by:
I'm considering switching to C# and using VS2005, but I'd like to know if I can have a list of values at runtime and then specify (at runtime) what it's value type is. For example, I would have...
4
by: Ram | last post by:
Hi All, I am new to this Generics. I started with a Generic method which doesn't return any value. It worked well. Then I tried to write a generic method which will sum up the given values...
9
by: Steve Richter | last post by:
in a generic class, can I code the class so that I can call a static method of the generic class T? In the ConvertFrom method of the generic TypeConvert class I want to write, I have a call to...
13
by: rkausch | last post by:
Hello everyone, I'm writing because I'm frustrated with the implementation of C#'s generics, and need a workaround. I come from a Java background, and am currently writing a portion of an...
26
by: raylopez99 | last post by:
Here is a good example that shows generic delegate types. Read this through and you'll have an excellent understanding of how to use these types. You might say that the combination of the generic...
0
by: =?Utf-8?B?TW9ydGVuIFdlbm5ldmlrIFtDIyBNVlBd?= | last post by:
"Anders Borum" wrote: Hi Anders, I'm afraid the GetMethod() does not currently support filtering on generic parameters so you will have to loop through the existing methods using...
3
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 3 Jan 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). For other local times, please check World Time Buddy In...
0
by: jianzs | last post by:
Introduction Cloud-native applications are conventionally identified as those designed and nurtured on cloud infrastructure. Such applications, rooted in cloud technologies, skillfully benefit from...
0
by: fareedcanada | last post by:
Hello I am trying to split number on their count. suppose i have 121314151617 (12cnt) then number should be split like 12,13,14,15,16,17 and if 11314151617 (11cnt) then should be split like...
0
by: stefan129 | last post by:
Hey forum members, I'm exploring options for SSL certificates for multiple domains. Has anyone had experience with multi-domain SSL certificates? Any recommendations on reliable providers or specific...
1
by: davi5007 | last post by:
Hi, Basically, I am trying to automate a field named TraceabilityNo into a web page from an access form. I've got the serial held in the variable strSearchString. How can I get this into the...
0
by: DolphinDB | last post by:
The formulas of 101 quantitative trading alphas used by WorldQuant were presented in the paper 101 Formulaic Alphas. However, some formulas are complex, leading to challenges in calculation. Take...
0
by: DolphinDB | last post by:
Tired of spending countless mintues downsampling your data? Look no further! In this article, you’ll learn how to efficiently downsample 6.48 billion high-frequency records to 61 million...
0
by: Aftab Ahmad | last post by:
So, I have written a code for a cmd called "Send WhatsApp Message" to open and send WhatsApp messaage. The code is given below. Dim IE As Object Set IE =...
0
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.