Geeks With Blogs
Rajiv Popat blog

A couple of days ago I was asked if there was any way by which I could overload a method to "return" more than one type. If I was in a class room, or I was teaching a programming class (I've done that for some part of my life :)) my instant answer would have been "No." And then I would have proceeded to explain why the whole idea was not valid in the Object Orientation world. 

However, this question was not coming from a high school student. In all probabilities it was coming based on a practical requirement and business problem.  My reaction to this question was: "why do you want to do something like that?" - At the first sound of it the question sounds like a fundamental High school Object Orientation question. But it isn't.

C# 2.0 and Generics is changing the fundamental way in which DotNet Programmers (me included; If I can can call myself a real programmer :)) are thinking. Any programmer worth his salt is asking himself some very basic and fundamental questions: How can I write better and lesser code? How can I write Generic Code? How can I write Code which I can reuse and leverage in situations which are "somewhat similar", even if they are not exactly the same. Anyway, I Digress...

So, this Post is about trying to answer that question. But before I attempt to answer it I would like to rephrase the question a little bit: The question (if you really think about it) that I heard was: 

"If I need to write the exactly same functions which have the exact same 'business logic' but need to return 5 different types do I really HAVE TO Write 5 different functions with completely different Names?"

My answer to that is: Not Really. I Fumble a bit with syntaxes and memory every now and then so I'm going to Google for syntaxes and give some basic code examples to explain my point as I type, but long story short I think the answer to this question is "Generic Methods with Constraints". If you know what these are and have already seen where I am going with this the rest of the post is not for you. If you don't know what these are Read on.

If You Goggled to this page to find the syntax of how you can have a different Constraint for each Generic Parameter and Return Type skip the rant and go to the "Yet Another Long Example" section.

With Generic Methods I Keep the method Generic (wow! that was easy to explain :)). In other words, While writing the method I don't specific what is the type of Parameters it expects or the Type of object it returns. The following code snippet illustrates:
 

   18         public static T WhatsGreater(T x, T y)

   19         {

   20                 // Implementation

   21         }

Worth noticing that in the above code all I am telling the compiler is: "Function WhatsGreater is going to Expect two Parameter of Type T and return an object of Type T where the calling code is going to tell you what T is".

Now, we do have a problem here. Since we've told the compiler nothing about T it means that the calling code can specify T to be any class or type. And Since I know nothing about T when I am writing this function it makes it really difficult for me to implement this function. In other words, while writing the implementation I have to "assume" that T is could be anything. It could even be an object of the "object" type.

This is where Constraints come into play. By Using Constraints I narrow down the possibilities of what T can be. For Example, I could tell he compiler.  "Function WhatsGreater is going to Expect two Parameter of Type T and return an object of Type T where the calling code is going to tell you what T is, BUT T HAS TO Implement the IComparable Interface!" The following Code Illustrates:
 

   19         public static T WhatsGreater(T x, T y) where T : IComparable

   20         {

   21                 // Implementation

   22         }

Now that I can be sure that both x and y are going to implement the IComparable Interface (because they're objects of T), I can actually "compare" them by assuming that they will implement the methods IComparable Expects them to implement (methods like CompareTo). Following Code Snippet Illustrates:
 

   18         public static T WhatsGreater(T x, T y) where T : IComparable

   19         {

   20             if (x.CompareTo(y) > 0) // if x is greater than y

   21                 return x;

   22             else return y;

   23         }

So, what we now have is a generic function that I can use with Any object that uses the IComparable Interface! Let's try calling this was two integers:
 

    9         static void Main(string[] args)

   10         {

   11             int GreaterInteger = WhatsGreater<int>(40,15);

   12             Console.WriteLine("Greater Integer is : " + GreaterInteger.ToString());

   13             Console.Read(); // Wait Till I See the Output

   14         }

In the above code snippet, notice that I specify that T is an integer (in the calling code) and that I am Passing two integers to WhatsGreater method when I say "WhatsGreater(40,15)" - Alternately I could have also specified that T is a double. The following Code snippet illustrates:
 

    9         static void Main(string[] args)

   10         {

   11             double GreaterDouble = WhatsGreater<double>(40.0,15.0);

   12             Console.WriteLine("Greater Double is : " + GreaterDouble .ToString());

   13             Console.Read(); // Wait Till I See the Output

   14         }

So, Apparently I can use the same "WhatsGreater" function in a Type Safe Manner and Make it return different Data Types without resorting to any boxing / un-boxing.

Now, I spend a couple of minutes on a Rattle which explains why this code is better than the convectional way of doing it. If you're already convinced that it is better, feel free to skip the next few code snippets and go to the "Another Long Example" section.

To any object oriented programmer this doesn't sound like a big deal. Does it? You could do this same thing with fundamentals of inheritance. The below code snippet would achieve the exactly same thing without generics:
 

   19         public static IComparable WhatsGreaterWithoutGenerics(IComparable x, IComparable y)

   20         {

   21             if (x.CompareTo(y) > 0) // if x is greater than y

   22                 return x;

   23             else return y;

   24         }

And the calling Code:
 

    9         static void Main(string[] args)

   10         {

   11            int t  = (int) WhatsGreat(5, 4);

   12            Console.WriteLine(t.ToString());

   13            Console.Read();

   14         }

This old technique works too, much like the generic method implementation BUT It's not type safe. In other words if I changed the implementation of WhatsGreaterWithoutGenerics method to return a double instead of an int (which the calling code is "expecting") it would result in a runtime exception. The following code snippet illustrates:
 

    9         static void Main(string[] args)

   10         {

   11            int t = (int)WhatsGreaterWithoutGenerics(5, 4);

   12             // The above line results in a Runtime Exception!

   13            Console.WriteLine(t.ToString());

   14            Console.Read();

   15         }

   16 

   17         public static IComparable WhatsGreaterWithoutGenerics(IComparable x, IComparable y)

   18         {

   19         // just return a double

   20             double returnValue = 4.3;

   21             return returnValue;

   22         }

Now, let's do the same thing on the Generic Method:
 

    9         static void Main(string[] args)

   10         {

   11            int i = WhatsGreater<int>(5, 4);

   12            Console.WriteLine(i.ToString());

   13            Console.Read();

   14         }

   15 

   16         public static T WhatsGreater(T x, T y) where T : IComparable

   17         {

   18             // Let's try returning a double

   19             double g = 5;

   20             return g;

   21             // This Results in a Compilation Error Because this method is type safe!

   22         }

Obviously this doesn't even compile. Because the method is Type Safe and it has to return an object of T i.e. x or y. The point that I'm trying to make from this long story is that A Generic method could return more than one type of object (depending on how you call it) in a Type Safe manner.

Yet Another Long Example

When talking about fundamental concepts I prefer not to use too much code. It just confuses things badly. But now that I've explained the basic concept I'm trying to write another example to show how the same concepts explained above could be utilized to address something very different.

Below code snippet contains three basic interfaces, A Guy and A Girl (who are going to get married, and when they do the last name of the girl will change to that of the guy's):
 

   32     public interface IGuy

   33     {

   34         // The FirstName of the Guy Followed by A Space Followed By the Last Name

   35         string GuysName { get; set; }

   36     }

   37 

   38     public interface IGirl

   39     {

   40         // The FirstName of the Guy Followed by A Space Followed By the Last Name

   41         string GirlsName { get; set; }

   42     }

And their Implementation:
 

   52     public class Guy : IGuy

   53     {

   54         private string _GuysName = "";

   55         public string GuysName

   56         {

   57             get { return _GuysName;  }

   58             set { _GuysName = value; }

   59         }

   60     }

   61     public class Girl : IGirl

   62     {

   63         private string _GirlsName = "";

   64         public string GirlsName

   65         {

   66             get { return _GirlsName; }

   67             set { _GirlsName = value; }

   68         }

   69     }

Now, we write a Generic function which could take any classes derived from the Interfaces and would return the Girls Name After she marries the guy:
 

   20         public static TypeGirl GetWifeNameAfterMarriage

   21             (TypeGuy guy, TypeGirl girl) where TypeGirl : IGirl where TypeGuy : IGuy

   22         {

   23             string GirlsFirstName = (girl.GirlsName.Split(' '))[0];

   24             string GuysLastName = (guy.GuysName.Split(' ')[1]);

   25             girl.GirlsName = GirlsFirstName + " " + GuysLastName;

   26             return girl;

   27         }

Notice how each parameter has been limited using a separate constraint. What this function basically does is, allows the calling code to specify any class that derives out of IGuy class and any class that derives out of IGirl class and then pass their objects to this this function. Once the objects are passed this generic function takes the first name of the girl and the last name of the guy and returns the girls full name After their marriage.

Finally we write the calling function:
 

    9         static void Main(string[] args)

   10         {

   11             Guy guy = new Guy();

   12             guy.GuysName = "Joe Smith";

   13             Girl girl = new Girl();

   14             girl.GirlsName = "Anna Williams";

   15             girl = GetWifeNameAfterMarriage<Guy, Girl>(guy, girl);

   16             Console.WriteLine(girl.GirlsName);

   17             Console.ReadLine(); // Output is Anna Smith

   18         }

Besides the first example the intent of this example was to show that you can actually set make a generic function expect more than one generic types and then set constraint on each of those generic types individually.

I've seen a lot of people focus on Generic classes and use them for custom collections but Generic Methods have many other potential uses than just custom collections. In fact, they're the first step of moving of your code to generic classes, if you think about it. I hope this long explanation helps clear out a few basic concepts. There are many other crazy things that can can be done with Generic functions but a sleepy brain and tired fingers won't let me type more (at least not on this post).

These are just my two cent and I don't claim to be a Generic Guru :) but all comments / suggestions / feedback and questions (if any) are always welcome from anyone who reads this.

Posted on Saturday, September 9, 2006 1:14 PM Cookbooks And Articles , Quick Tips And Solutions | Back to top


Comments on this post: Back To The Basics And Beyond! Generic Methods and Multiple Constraints.

No comments posted yet.
Your comment:
 (will show your gravatar)


Copyright © Rajiv Popat | Powered by: GeeksWithBlogs.net