可空的<t>作为参数

时间:2019-04-11 17:12:17

标签: c# generics types abstract nullable

我一直在尝试为一个人事图书馆项目弄乱泛型和抽象,但是我遇到了一个问题。 我发现this post有点像我想做的事,但我想将其进一步推进。因为我想将泛型参数的功能限制为仅几种类型 像这样:

public static T Read<T>(T? min, T? max) where T: int, float, double, anything i want
{
}

我知道这种方式是不可能的,但是我正在尝试找到一些解决方法来实现类似的目的

我尝试设置为使用:T? 但我收到一条消息,说T不能为null才能用作参数。 如您所见:

where F : ConsoleReadType<T>

我基本上是在尝试只允许继承的类运行。

public abstract class ConsoleReadType<T>
{
    public abstract T Read();
    public abstract T Read(T? min, T? max);
    public virtual F ReadUntilCorrect<F>(Func<F> FunctionToRun, string message = "") /*where F : ConsoleReadType<T>*/
    {
        while (true)
        {
            try
            {
                return FunctionToRun();
            }
            catch (ConsoleInputException)
            {
                if (!string.IsNullOrEmpty(message))
                    ConsoleWrite.Error(message);
            }
        }
    }
}

public class ConsoleReadDouble : ConsoleReadType<double>
{
    public override double Read()
    {
        if (!double.TryParse(Console.ReadLine().Replace(".", ","), out double ret))
        {
            throw new ConsoleInputException();
        }
        return ret;
    }
    public override double Read(double? min, double? max)
    {
        if (!double.TryParse(Console.ReadLine().Replace(".", ","), out double ret))
        {
            throw new ConsoleInputException("invalid input format");
        }
        if (min.HasValue && ret < min || max.HasValue && ret > max)
        {
            throw new ConsoleInputException("input value should be between: " + min + " and " + max);
        }
        return ret;
    }
}

主要问题是:
1.有没有一种方法可以抽象地设置可为空的T变量,还是有一种更好的方法来实现我想要的工作?
2.我可以只允许某些带有where语句的功能吗?
3.是否有一种方法可以使这些类最终成为静态类以用作辅助对象而不必实例化它们?
4.我也对您能给我的有关我的代码的任何建议感兴趣

非常感谢。

2 个答案:

答案 0 :(得分:3)

您可以仅使用此

// add where T: struct so that only structs (int, double, etc) can be used
// allows you to use T? 
public abstract class ConsoleReadType<T> where T: struct
{
    public abstract T Read();
    public abstract T Read(T? min, T? max);
    public virtual T ReadUntilCorrect(Func<T> FunctionToRun, string message = "")
    {
        while (true)
        {
            try
            {
                return FunctionToRun();
            }
            catch (ConsoleInputException)
            {
                if (!string.IsNullOrEmpty(message))
                    ConsoleWrite.Error(message);
            }
        }
    }
}

  

有没有一种方法可以使这些类最终成为静态类,而无需实例化它们呢?

不是,您不能从静态类继承,因此必须删除ConsoleReadType<T>类。但是,您可以使用Factory方法:

public static class ConsoleReader
{
    public static ConsoleReadType<T> GetReader<T>()
    {
        if (typeof(T) == typeof(double))
        {
            return new ConsoleReadDouble();
        }
        // etc
    }
}

  

您也可以向我提供有关我的代码的任何建议,我也很感兴趣

我认为您根本不需要Read();Read(T? min, T? max);就足够了。然后,ReadUntilCorrect不应收到Func<T>,而应呼叫Read。您可以这样做:

public abstract class ConsoleReadType<T> where T: struct
{
    public abstract T Read(T? min = null, T? max = null);
    public virtual T ReadUntilCorrect(T? min = null, T? max = null)
    {
        while (true)
        {
            try
            {
                return Read(min, max);
            }
            catch (ConsoleInputException ciex)
            {
                ConsoleWrite.Error(ciex.Message);
            }
        }
    }
}

答案 1 :(得分:1)

您可以使用struct constraintwhere T: struct)将泛型类型限制为值类型。然后,您可以使用Nullable<T> / T?

public abstract class ConsoleReadType<T> where T: struct
{
    public abstract T Read();
    public abstract T Read(T? min, T? max);
}

实现ReadUntilCorrect并能够使用静态方法调用的一个技巧是将具体的继承类用作抽象基类的类型参数:

public abstract class ConsoleReadType<T, ConcreteReaderT> 
 where T: struct 
 where ConcreteReaderT: ConsoleReadType<T, ConcreteReaderT>, new()
{
    public abstract T Read();
    public abstract T Read(T? min, T? max);

    public static T ReadUntilCorrect(string message = "") 
    {
        ConcreteReaderT reader = new ConcreteReaderT();
        while (true)
        {
            try
            {
                return reader.Read();
            }
            catch (ConsoleInputException)
            {
                if (!string.IsNullOrEmpty(message))
                    Console.Error.WriteLine(message);
            }
        }
    }
}

// Use the concrete class as its own type parameter so that
// the base class can use the concrete class without knowing it 
// beforehand
public class ConsoleReadDouble : ConsoleReadType<double, ConsoleReadDouble>
{
    public override double Read()
    {
        if (!double.TryParse(Console.ReadLine().Replace(".", ","), out double ret))
        {
            throw new ConsoleInputException();
        }
        return ret;
    }
    public override double Read(double? min, double? max)
    {
        if (!double.TryParse(Console.ReadLine().Replace(".", ","), out double ret))
        {
            throw new ConsoleInputException("invalid input format");
        }
        if (min.HasValue && ret < min || max.HasValue && ret > max)
        {
            throw new ConsoleInputException("input value should be between: " + min + " and " + max);
        }
        return ret;
    }
}

然后可以使用如下具体类:

class Program
{
    static void Main(string[] args)
    {
        double d = ConsoleReadDouble.ReadUntilCorrect("Please enter a valid number");

    }
}