Question

I've run into an annoyance in c#: I've got two almost exact functions; The only thing different in the functions is their return type.

If possible, how do you implement a function, which will have the return-type decided by the call?

The two function are in the two blocks below:

static public int GetNumberFromUser(string Info)
{
    int TheDesiredNumber;

    while (true)
    {
        Console.Write("Please type " + Info + " : ");

        if (int.TryParse(Console.ReadLine(), out TheDesiredNumber))
        {
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine(" - " + Info + " is set to " + TheDesiredNumber.ToString() + "!");
            Console.ForegroundColor = ConsoleColor.Gray;
            return TheDesiredNumber;
        }

        WrongInput(" - Invalid input!");
    }
}

static public double GetNumberFromUser(string Info)
{
    double TheDesiredNumber;

    while (true)
    {
        Console.Write("Please type " + Info + " : ");

        if (double.TryParse(Console.ReadLine(), out TheDesiredNumber))
        {
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine(" - " + Info + " is set to " + TheDesiredNumber.ToString() + "!");
            Console.ForegroundColor = ConsoleColor.Gray;
            return TheDesiredNumber;
        }

        WrongInput(" - Invalid input!");
    }
}
Was it helpful?

Solution

The answer to your question is Yes... it is possible. Your question indicates that the only difference is the return type (which is handled with generics), but that isn't true. The other difference is the TryParse function also relies on a type. So, you need a way to specify a type safe TryParse function which is handled with a delegate.

So, use a combination of generics and a delegate. Specify the type you want to use between the brackets. Define a delegate called TryParse which is also typed. Then, you can pass the function which does the parsing. I have included a complete example for a console application. If you look at the main, then it is a simple function call where you pass the proper TryParse function. What is cool about this method is that you can have TryParse for any type. If you create your own rational class or a fraction class, you can pass in the function for your class.

using System;

namespace DelegateSample
{
    public class Program
    {
        // delegate to handle tryparse
        public delegate bool TryParse<T>(string txt, out T desiredNumber);

        // generic function that will get a number from a user and utilize the existing TryParse for the specified type
        public static T GetNumberFromUser<T>(string info, TryParse<T> tryParseFunction)
        {
            T TheDesiredNumber;

            while (true)
            {
                Console.Write("Please type " + info + " : ");

                string input = Console.ReadLine();

                // use the delegate here to run the TryParse, which is passed in
                if (tryParseFunction(input, out TheDesiredNumber))
                {
                    Console.ForegroundColor = ConsoleColor.Green;
                    Console.WriteLine(" - " + info + " is set to " + TheDesiredNumber.ToString() + "!");
                    Console.ForegroundColor = ConsoleColor.Gray;
                    return TheDesiredNumber;
                }

                // WrongInput isn't defined, this should suffice for the sample
                Console.WriteLine(input + " - Invalid input!");
            }
        }

        public static void Main(string[] args)
        {
            // this can be used for any function which implements the TryParse function which matches the delegate
            // it is a simple function call.  Specify the type between the brackets, and then pass the function in that
            // does the TryParse.  You could even write your own TryParse for your own classes, if needed.
            int iVal = GetNumberFromUser<int>("integer", int.TryParse);
            double dVal = GetNumberFromUser<double>("double", double.TryParse);
            float fVal = GetNumberFromUser<float>("float", float.TryParse);
        }
    }
}

OTHER TIPS

This works but it makes me cringe. Too bad C# doesn't allow a generic to specify multiple acceptable types for T.

static public T GetNumberFromUser<T>(string Info)
{
    Type t = typeof(T);
    if (t.Equals(typeof(int)) || t.Equals(typeof(double)))
        return (T)GetNumberFromUser2<T>(Info);
    throw new ArgumentException(string.Format("GetNumberFromUser<T> only works with int and double. Type '{0}' is not valid.", t.Name));
}
static private object GetNumberFromUser2<T>(string Info)
{
    object TheDesiredNumber = null;
    Type t = typeof(T);
    while (true)
    {
        Console.Write("Please type " + Info + " : ");

        if (t.Equals(typeof(int)))
        {
            int TheDesiredInt;
            if (int.TryParse(Console.ReadLine(), out TheDesiredInt))
            {
                TheDesiredNumber = TheDesiredInt;
            }
        }
        else if (t.Equals(typeof(double)))
        {
            double TheDesiredDouble;
            if (double.TryParse(Console.ReadLine(), out TheDesiredDouble))
            {
                TheDesiredNumber = TheDesiredDouble;
            }
        }

        if (TheDesiredNumber != null)
        {
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine(" - " + Info + " is set to " + TheDesiredNumber.ToString() + "!");
            Console.ForegroundColor = ConsoleColor.Gray;
            return TheDesiredNumber;
        }

        WrongInput(" - Invalid input!");
    }
}

Use out parameter:

static public void GetNumberFromUser(string Info, out int number);
static public void GetNumberFromUser(string Info, out double number);

or use different names:

static public int GetIntFromUser(string Info);
static public double GetDoubleFromUser(string Info);

or declare output as object:

static public object GetNumberFromUser(string Info);

or use generic:

static public T GetNumberFromUser<T>(string Info)
{
    // ...
    return (T)(object)TheDesiredNumber;
}

This is possible using generics and a little help of extension methods & reflection here is an example:

public static class Extensions
{
    public static bool TryParse<T>(this string source, out T result)
        where T : struct
    {
        result = default(T);
        var method = typeof (T)
            .GetMethod("TryParse", new [] {typeof (string), typeof (T).MakeByRefType()});

        if(method == null) return false;

        bool isValid =  (bool)method
            .Invoke(null, new object[] {source, result});

        if (isValid) return true;

        return false;
    }
}

This generic TryParse allows us to call TryParse on any type of struct.So then we can generalize GetNumberFromUser method and get the result like this:

public static T GetNumberFromUser<T>(string Info)
    where T : struct
{
    T TheDesiredNumber = default(T);

    while (true)
    {
        Console.Write("Please type " + Info + " : ");

        // this it the key point
        if (Console.ReadLine().TryParse<T>(out TheDesiredNumber)) 
        {
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine(" - " + Info + " is set to " + TheDesiredNumber.ToString() + "!");
            Console.ForegroundColor = ConsoleColor.Gray;
            return TheDesiredNumber;
        }

        WrongInput(" - Invalid input!");
}

Usage:

var result = GetNumberFromUser<int>("Integer");
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top