函数调用决定函数';s返回类型

本文关键字:返回类型 决定 函数 函数调用 | 更新日期: 2023-09-27 17:58:59

我在c#中遇到了一个麻烦:我有两个几乎完全相同的函数;函数中唯一不同的是它们的返回类型。

如果可能的话,如何实现一个函数,该函数的返回类型将由调用决定?

这两个功能在下面的两个块中:

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!");
    }
}

函数调用决定函数';s返回类型

您的问题的答案是肯定的。。。这是可能的。您的问题表明,唯一的区别是返回类型(用泛型处理),但事实并非如此。另一个区别是TryParse函数也依赖于一个类型。因此,您需要一种方法来指定类型安全的TryParse函数,该函数由委托处理。

因此,使用泛型和委托的组合。指定要在方括号之间使用的类型。定义一个名为TryParse的委托,该委托也是类型化的。然后,您可以传递进行解析的函数。我已经包含了一个控制台应用程序的完整示例。如果您查看main,那么它是一个简单的函数调用,您可以在其中传递正确的TryParse函数。这个方法的酷之处在于,您可以对任何类型使用TryParse。如果创建自己的rational类或fraction类,则可以为类传递函数。

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);
        }
    }
}

这很有效,但让我感到尴尬。糟糕的是,C#不允许泛型为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!");
    }
}

使用out参数:

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

或者使用不同的名称:

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

或将输出声明为object:

static public object GetNumberFromUser(string Info);

或使用通用:

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

使用泛型和一些扩展方法&反射是一个例子:

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;
    }
}

这个通用的TryParse允许我们在任何类型的结构上调用TryParse。因此,我们可以推广GetNumberFromUser方法,得到如下结果:

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!");
}

用法:

var result = GetNumberFromUser<int>("Integer");