Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Languages / C# / March 2008

Tip: Looking for answers? Try searching our database.

How to make my methods dynamic?

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Tino Donderwinkel - 26 Mar 2008 10:01 GMT
Hi,

Currently I have the the following methods in my class;

public Circle LoadCircle(int a) {
   Circle result = new Circle();
   ...
   return result;
}
public Cube LoadCube(int a) {
   Cube result = new Cube();
   ...
   return result;
}

I want to make a Dynamic Method 'Load' in stead of the two methods I
currently have. I tried stuff like this;

public T Load<T>(int a) {
   // here, result is determined to be either a Circle or a Cube
   // triedstuff like this, but that obviously doesn't work: switch
(typeof(T)) { case (typeof(Circle)): break; case (typeof(Cube)): break;}
   return result;
}

Problems I run into is that there is no conversion between types 'T' and
Circle/Cube. This can be fixed by returning an 'object' and casting it on
the other side. But I do not want to do that.

Furthermore, the switch statement I use won't let me use typeof(Cube) etc.
in the case constructor. This can be done with multiple 'if' statements, but
that's a 'dirty' solution.

Is there a way to solve this?

Tino Donderwinkel
Exchange Server MVP
Marc Gravell - 26 Mar 2008 10:15 GMT
In this case, yes:

public T Load<T>(int a) where T : new() {
   T newItem = new T();
   ...
   return newItem;
}

However, to do something useful in the "..." you might need a common
base-class or interface between the different T - for example:

   public static T Load<T>(int a) where T : IShape, new()
   {
       T newItem = new T();
       newItem.Foo = a;
       return newItem;
   }

with:

   public interface IShape { int Foo { get; set; } }
   public class Circle : IShape { public int Foo { get; set; } }
   public class Cube : IShape { public int Foo { get; set; } }

allows you to call:

       Cube cube = Load<Cube>(32);
       Circle circle = Load<Circle>(1);

but will prevent you (at compile-time) from calling Load<int>(15) etc,
as int doesn't satisfy ": IShape"

Generics doesn't offer an easy answer to non-default constructors -
i.e. if you need new Cube(a) etc. There are workarounds but they
aren't ideal; but post back if you need more info.

Marc
Tino Donderwinkel - 26 Mar 2008 10:46 GMT
Thanks for you fast response!

In my case, the method will return structs.

E.g.

   public struct Circle
   {
       public int radius;
       public int x;
       public int y;
   }

   public struct Cube
   {
       public int l;
       public int x;
       public int y;
   }

Please note; this is an example. The real structs are quit complex, and
aren't called Cube and Circle... They are very different. Because of this,
I'm not sure if defining the interface will do me any good... Will it?

For starters, I would like to use the existing methods in the new generic
method;

public T Load<T>(int a)
{
   if (typeof(T) == typeof(Circle))
   {
       return LoadCircle(a);
   }
   else if (typeof(T) == typof(Cube))
   {
       return LoadCube(a);
   }
   else
   {
       throw new Exception("Invalid Type Specified.");
   }
}

I'd prefer a switch statement, since the method should be used for about 12
structs, but I think the switch statement won't really let me do that...
because of the typeof(T) etc.

I'm very new to generics. :-) never used it, besides the class List<T>...
Haha.

Thanks for you help,

Tino Donderwinkel
Exchange Server MVP

> In this case, yes:
>
[quoted text clipped - 33 lines]
>
> Marc
Jon Skeet [C# MVP] - 26 Mar 2008 10:56 GMT
<snip>

> I'd prefer a switch statement, since the method should be used for about 12
> structs, but I think the switch statement won't really let me do that...
> because of the typeof(T) etc.
>
> I'm very new to generics. :-) never used it, besides the class List<T>...
> Haha.

Sounds like *really* you want a dictionary of delegates used to build
the objects. You'll end up with some boxing in there unfortunately,
but I can't see how that's avoidable. You'd have something like:

static readonly Dictionary<Type,Func<object>> factories = new
Dictionary<Type,Func<int,object>>
{
   { typeof(Circle), x => LoadCircle(x) },
   { typeof(Cube), x => LoadCube(x) }
};

public T Load<T> (int i)
{
   Func<int,object> factory;
   if (!factories.TryGetValue(typeof(T), out factory))
   {
       throw new ArgumentException("Invalid type specified");
   }
   object ret = func(i);
   return (T) ret;
}

At this point you don't really get much in the way of benefits from
generics to be honest - the above could all be done with a normal Type
parameter (except for the cast, which would have to be at the call
site).

It should work though...

Jon
Tino Donderwinkel - 26 Mar 2008 11:16 GMT
Thanks!

I'll try that!

Tino Donderwinkel
Exchange Server MVP

> <snip>
>
[quoted text clipped - 36 lines]
>
> Jon
Tino Donderwinkel - 26 Mar 2008 12:34 GMT
Thanks.

It worked out.

I now have the following code;

       private readonly Dictionary<Type, Func<object, object>> load = new
Dictionary<Type, Func<object, object>>
           {
               {typeof(VoiceGroup), x => LoadVoiceGroup((int)x)},
               {typeof(Service), x => LoadService((int)x)},
               {typeof(Operator), x => LoadOperator((int)x)},
               {typeof(CPEGroup), x => LoadCPEGroup((int)x)},
               {typeof(User), x => LoadUser((string)x)},
               {typeof(Client), x => LoadClient((string)x)}
           };

           public T Load<T>(object identifier) where T : struct
       {
           Func<object, object> result;
           if (!load.TryGetValue(typeof(T), out result))
           {
               throw new Exception("The object type '" +
typeof(T).ToString() + "' is invalid.");
           }
           try
           {
               return (T)result(identifier);
           }
           catch (InvalidCastException error)
           {
               throw new Exception("The identifier type for loading an
object of type '" + typeof(T).ToString() + "' is invalid.");
           }
       }

This works!

Now see if I can replace similar functions, that return a List<T> (with 'T'
the types as in the code)... These take 2 parameters...

Tino

> <snip>
>
[quoted text clipped - 36 lines]
>
> Jon
Marc Gravell - 26 Mar 2008 12:56 GMT
Personally I'm not sure that this is going in the right direction...
you've introduced boxing and taken away the ability for the caller to
know what type to pass the method (and the compiler's ability to
enforce it) - i.e. when loading a user do I give it an int, a string,
or a bool? dunno (without looking). It also isn't clear that it
*can't* load a "Foo", a "Bar" or a "Flibble".

I'm not sure you have gained much from the caller invoking
LoadUser("abc") directly, rather than Load<User>("abc") (guessing the
arg-type). There might be some use-cases if you are deep in the bottom
of some highly generic code, but equally there may be cleaner
solutions if the actual use-case is clear.

It is perhaps unfortunate (then again, perhaps not) that C# doesn't
offer return-type overloading. But if the "LoadUser" etc methods
(instead of "Load") offend (and I can't say that they offend me), then
one final option might be using "out" to enable overloading...

public void Load(string id, out User user) {... load the user...}
public void Load(int id, out VoiceGroup group) {...load the group...}

etc

Then:
User user;
Load(123, out user);

will pick up the correct overload at compile-time.

But I'd go for the simplest "User LoadUser(int id)" approach until
there is a known reason not to...

Marc
Tino Donderwinkel - 26 Mar 2008 13:14 GMT
Thank you for the warning.

I'm not sure what way to go.

In the current code I have a ton of methods. For each of the 6 structs I
have, I have these methods;

Load{struct}(int/string id)
Save{struct}(int/string id)
Update{struct}(int/string id, int/string oldid)
Get{struct}List()
Get{struct}List(int start, int count)
Remove{struct}(int/string id)
Get{struct}Log()
Get{struct}Log(int/string id)
Get{struct}Log(int start, int count)
Get{struct}Log(int/string id, int start, int count)
Verify{struct}(int/string id)
Verify{struct}(int/string id, int/string oldid)

That's 12 x 6 = 72 methods. :-(

It's just a pain to maintain these. Furthermore, it's a pain to document.
:-) Even though I use sandcastle...

For testing now, I have ONE single Load function. This Load function
replaces;
Load{struct}
Get{struct}List
Get{struct}List(int start, int count)
for all {struct}!

I have noticed that by doing this, I introduced other potential problems.
But I'm just not sure what way to go with this... Having all these methods
is a pain as well... wouldn't you say? I'm really looking for a 'best
practice'... I can go both ways...

Tino

> Personally I'm not sure that this is going in the right direction...
> you've introduced boxing and taken away the ability for the caller to
[quoted text clipped - 29 lines]
>
> Marc
Marc Gravell - 26 Mar 2008 13:37 GMT
Well, from you "Thanks. It worked out." post (and talking about Load
still), you still *have* these 6 methods... you've just added another
one on top:

               {typeof(VoiceGroup), x => LoadVoiceGroup((int)x)},
               {typeof(Service), x => LoadService((int)x)},
               {typeof(Operator), x => LoadOperator((int)x)},
               {typeof(CPEGroup), x => LoadCPEGroup((int)x)},
               {typeof(User), x => LoadUser((string)x)},
               {typeof(Client), x => LoadClient((string)x)}

plus Load<T>

Without more info it is hard to tell how much of these 12x6 sub-
methods is shared and could be sensibly refactored to share some
internals. It might also be that ORM tools offer some of this for you
(particularly the load/save).

But please note the original warning about using structs; when talking
about graphics that tends to just about (at a stretch) be inside the
small set of cases when a mutable struct makes sense. But! Things like
"User", "Client", "Operator" etc - they sound 100% like they should be
classes, not structs. You might have some good reason why this isn't
so, but for your own sanity - make sure you understand the difference.
Mutable structs are a common cause of bugs and questions.

Marc
Tino Donderwinkel - 26 Mar 2008 13:50 GMT
Thanks for clarifying.

The class that holds all these methods is a 'wrapper' for a piece of code
that consumes a SOAP interface to a web service. It handles the HTTP
sessions, logging in and out and some more soap specific stuff. It makes
'talking' to the (not too well defined) SOAP interface more easy for the
client.

The structs map to the structs defined in the WSDL, and differ quit
significantly. Although a CLIENT and a USER might seem like the same thing,
more or less, these are completely different entities. A client for example
has multiple other required structs, and nullable values. A user is what you
might expect; three strings or so in a simple struct.

I'll try to find some documentation on designing guidelines etc.

Thanks all for taking the time to help me out on this one. It's appreciated.

Tino Donderwinkel
Exchange Server MVP

> Well, from you "Thanks. It worked out." post (and talking about Load
> still), you still *have* these 6 methods... you've just added another
[quoted text clipped - 23 lines]
>
> Marc
Marc Gravell - 26 Mar 2008 11:21 GMT
> I'm not sure if defining the interface will do me any good... Will it?

It really depends on what you are doing inside the "..." - i.e. are
you doing something common to them. If you aren't, then generics might
not be the best approach.

Note that there is a little alarm going off in my head "mutable
struct, mutable struct, ...".

Just to warn that you need to be *really* careful with these... if you
are very sure that you know what you are doing, then fine - but not
for the faint hearted. In most cases (especially when mutable) a class
would be preferable. Can I invite you to check that you really mean
this... hint: they aren't the same as C++ structs.

Marc
Tino Donderwinkel - 26 Mar 2008 10:51 GMT
Hmm..

I could use:

return (T)(object)LoadCircle(id);
and
return (T)(object)LoadCube(id);

is that sane?

Tino

> In this case, yes:
>
[quoted text clipped - 33 lines]
>
> Marc

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.