可空 <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);
}
}
}
}
我一直在尝试为个人图书馆项目搞乱通用类型和抽象,但我遇到了一个问题。 我发现 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);
}
}
}
}