Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

Here is my problem. Do I need to give a new identifier to each delegate I write of the following delegate type? like so: or could i use one delegate that accounts for any Datatype I need to use so i don't have to keep repeating the code?

Something that may or may not be relevant: I'm calling these functions to prompt user's input for new fields to add for records in a database. So database field types are the data types I'm dealing with, but I'm using this approach for learning purposes to try and minimize redundancy.

public static class stcHelper  // helper class
{
    public delegate T DelReturnType<T>();
    static public DelReturnType<int> UserInt = () =>
    {
        Console.Write("Enter integer: ");
        return int.Parse(Console.ReadLine());
    };
    static public DelReturnType<string> UserString = () =>
    {
        Console.Write("Enter string: ");
        return Console.ReadLine();
    };
    static public DelReturnType<AnotherDataType> UserAnotherDataType = () =>
    {
        Console.Write("Enter AnotherDataType: ");
        return SomeKindOfConversionFunction(Console.ReadLine());
    };
    // and so on for what could be 15 other data types ..
}

or is there a better way to reduce redundant code?

The code below is also part of the same class.

public static class stcHelper
{
    static public T InsistValidInput<T>(DelTypeReturn<T> MyInputAction)
    {
        bool bSuccess = true;
        do
        {
            try
            {
                return MyInputAction();
            }
            catch (Exception ex)
            {
                Console.WriteLine("Invalid input!" + ex.ToString());
                bSuccess = false;
            }
        } while (!bSuccess);
        return default(T);
    }
}
share|improve this question

1 Answer 1

up vote 15 down vote accepted

If done cleverly, you can get away with a single method to get the input of any type:

static T GetInput<T>(string message, Converter<string, T> transform)
{
    Console.WriteLine(message);
    return transform(Console.ReadLine());
}

All you need to do is pass in a transformation function (the Converter<TInput,TOutput> delegate is equivalent to Func<T,TResult>):

int parsedInt = GetInput("Type in a number", int.Parse);
float parsedFloat = GetInput("Type in a float", float.Parse);

And ta-da, you've got your input, parsed as the desired type.

But as you also noted in the second half of your question, it's dangerous to assume that the input will always be valid. There's a universal version of your insist-on-valid-input function too. However, you should avoid using exceptions for normal control flow. Use the xxx.TryParse methods instead.

First, define the following delegate that matches the TryParse method defined in all the primitive types:

delegate bool TryParse<T>(string input, out T output);

Then, you can implement the loop:

static T InsistOnValidInput<T>(string message, string errorMessage, TryParse<T> validator)
{
    Console.WriteLine(message);
    T result;
    while (!validator(Console.ReadLine(), out result))
    {
        Console.WriteLine(errorMessage);
    }
    return result;
}

And call it:

bool parsedBool = InsistOnValidInput<bool>("Type in a bool", "Try again", bool.TryParse);

Unfortunately, C# doesn't infer the type parameter of the output argument, so you need to specify it manually (in this case, <bool>).

share|improve this answer
    
I am reading your code, and wondering why you have 2 generics inside the 'Func<string, T>' ? Also, when you call the transform Func, you don't include <>, but include () with a parameter inside it? –  Matt Rohde Oct 31 '12 at 1:29
1  
Func<string,T> is a .NET delegate describing a method that takes a single argument of type string and returns a result of type T. float.Parse is such a method, so it can be used as a transform. It follows that we can call transform with the result of Console.ReadLine and it will return T. We don't need to explicitly specify type arguments when they can be inferred by the compiler. I strongly encourage you to step through the code in the debugger line for line - everything will make sense. –  codesparkle Oct 31 '12 at 1:51
    
I see. by the way, in MSDN Library, it says public delegate TResult Func<in T, out TResult>(T arg). Was your positioning of T for the second parameter out TResult instead of for the first parameter in T intentional? If yes, then does that mean that T is determined by the output of the given Func<in T, out TResult>? –  Matt Rohde Oct 31 '12 at 2:37
    
the use of in and out in this case is confusing, but it has nothing to do with input or output parameters; instead, it means that T is is contravariant (real argument can be less derived) and TResult is covariant (real argument can be more derived) as described on msdn. Be sure to take a look at the delegates section of the covariance and contravariance guide as well. –  codesparkle Oct 31 '12 at 10:17
2  
There is actually a more specific delegate for conversion, called System.Converter<Tinput,Toutput>. It is ultimately just a Func<Tin,Tout>, but it is more specific to the task and can be re-used in several framework functions. –  Dan Lyons Oct 31 '12 at 17:15

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.