可空 作为参数

问题描述 投票:0回答:2

我一直试图搞乱人类图书馆项目的泛型类型和抽象,但我遇到了一个问题。我发现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.有没有办法让这些类最终成为一个帮助,而不必实例化它们? 我也对你能给我的关于我的代码的任何建议感兴趣

非常感谢。

c# generics types abstract nullable
2个回答
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
投票

您可以使用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");

    }
}
© www.soinside.com 2019 - 2024. All rights reserved.