I've been trying to mess around with generic types and abstractions for a personnal library project but i'm facing a problem. I found this post that was kinda like what i wanted to do, but I wanted to push it a step further. because i wanted to constrain my function with generic parameter to only few types something like :
public static T Read<T>(T? min, T? max) where T: int, float, double, anything i want
{
}
I know it's impossible in this way, but i'm trying to find some workarounds to achieve something similar
I tried to set to use: T?
but I get a message that says that T must not be nullable to be used as a parameter.
As you can see from :
where F : ConsoleReadType<T>
I'm basically trying to allow only inherited classes to run.
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;
}
}
So the main questions are:
1. Is there a way to set nullable T variables in abstract, or is there a better way to achieve what i'm trying to do?
2. Can I allow only certain functions with a where statement ?
3. Is there a way to make these classes static in the end to be used as a helper without having to instanciate them?
4. I'm also interested by any advice you could give me about my code
Thanks a lot.
You could use just this:
// 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?
Not really, you cannot inherit from a static class, so you'd have to remove the ConsoleReadType<T>
class. You could, however, use a Factory approach:
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
In my opinion, you don't need Read();
at all, Read(T? min, T? max);
should be enough. Then, ReadUntilCorrect
shouldn't receive a Func<T>
but instead call Read
. You could do with just:
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);
}
}
}
}