可空 <T> 作为参数

Nullable <T> as parameter

我一直在尝试为个人图书馆项目搞乱通用类型和抽象,但我遇到了一个问题。 我发现 this post 这有点像我想做的,但我想更进一步。因为我想用通用参数将我的函数限制为只有几种类型 像 :

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

我知道用这种方法是不可能的,但我正试图找到一些解决方法来实现类似的效果

我尝试设置为使用:T? 但我收到一条消息,指出 T 不能为空才能用作参数。 从 :

可以看出
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. 我也对你能给我的关于我的代码的任何建议感兴趣

非常感谢。

您可以使用 struct constraint (where 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 并能够使用静态方法调用的一个技巧是使用具体继承 class 作为抽象基础 class 的类型参数:

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

然后您可以像这样使用具体的 classes:

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

    }
}

你可以使用这个:

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

Is there a way to make these classes static in the end to be used as a helper without having to instanciate them?

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

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

I'm also interested by any advice you could give me about my code

在我看来,你根本不需要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);
            }
        }
    }
}